home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / ANIVGA.ZIP / ANIVGA.NEW < prev    next >
Text File  |  1994-09-22  |  247KB  |  7,554 lines

  1. {$A+,B-,D+,L+,N-,E-,O-,R-,S-,V-,G-,F-,I-,X-}
  2. {$M 16384,0,655360}
  3.  
  4. {Programtool for the realization of fast animations on the VGA-graphic-     }
  5. {card by: Kai Rohrbacher, 1988-1991, Turbo-Pascal 6.0                       }
  6.  
  7. { Features:                                                                 }
  8.  
  9. { - flickerfree animation by using page-flipping, watching retrace          }
  10. {   signals and usage of a special 256 color mode of the VGA                }
  11. { - sprite movement in any pixel-increments you want                        }
  12. { - arbitrary background image for the animation                            }
  13. { - full use of the VGA's 256-color mode                                    }
  14. { - several sprite display methods available:                               }
  15. {   - sprites can be declared to be transparent with regard to the background}
  16. {     or other sprites pixel by pixel                                                     }
  17. {   - sprites can change their color depending on the underlying background  }
  18. {     image (-> shadow function)                                            }
  19. { - pixel precise hit-detection routine for sprite collisions built in           }
  20. { - correct clipping of sprites at the screen boundaries when moving on- or }
  21. {   offscreen                                                               }
  22. { - up to 32767 different sprites (default: 1000)                           }
  23. { - up to 32767 sprites may be simultaneously active (default: 500)         }
  24. { - maximal size of each sprite = 64k                                              }
  25. { - maximal size of all loaded sprites only restricted by available memory  }
  26. { - works with virtual coordinates in the range -16000..+16000, thus        }
  27. {   simple creation of horizontal/vertical "scrolling" applications                }
  28. { - scrolling background image, too                                         }
  29. { - several supporting routines to: draw lines (with built in clipping-     }
  30. {   algorithm), points and graphic-text (incl. clipping), automatic heap    }
  31. {   management for storing/loading sprites, handling background images,     }
  32. {   changing sprite display modes at runtime, adjustment for different      }
  33. {   CPU-clocks, ...                                                         }
  34.  
  35. UNIT ANIVGA;
  36. INTERFACE
  37.  
  38. USES CRT,DOS;
  39.  
  40. CONST ANIVGAVersion=11; {version number}
  41.       NMAX=499;
  42.       XMAX=319;
  43.       YMAX=199;
  44.       LoadMAX=1000;  {max. number of simultaneously active sprites   }
  45.       LINESIZE=(XMAX+1) DIV 4;    {size of one row=80 bytes    }
  46.       PAGESIZE=(YMAX+1)*LINESIZE; {200 rows at 320/4 bytes each}
  47.       BACKGNDPAGE=2;
  48.       SCROLLPAGE=3;
  49.  
  50.       STATIC=0;      {constants for background mode }
  51.       SCROLLING=1;
  52.  
  53.       MaxTiles=10000;  {max. number of background tiles   }
  54.       StartVirtualX:INTEGER=0;  {upper left image corner   }
  55.       StartVirtualY:INTEGER=0;
  56.  
  57.       {supported display modes for sprites:        }
  58.       Display_NORMAL=0;   {normal  : transparent for color 0  }
  59.       Display_FAST  =1;   {fast    : don't take background into account}
  60.       Display_SHADOW=2;   {shadow  : color lookup driven by background data}
  61.       Display_SHADOWEXACT=3; {color 0 is transparent for shadows, too    }
  62.       Display_UNKNOWN=255;{error value}
  63.  
  64.       {error codes of the animation package: }
  65.       Err_None=0;
  66.       Err_NotEnoughMemory=1;
  67.       Err_FileIO=2;
  68.       Err_InvalidSpriteNumber=3;
  69.       Err_NoSprite=4;
  70.       Err_InvalidPageNumber=5;
  71.       Err_NoVGA=6;
  72.       Err_NoPicture=7;
  73.       Err_InvalidPercentage=8;
  74.       Err_NoTile=9;
  75.       Err_InvalidTileNumber=10;
  76.       Err_InvalidCoordinates=11;
  77.       Err_BackgroundToBig=12;
  78.       Err_InvalidMode=13;
  79.       Err_InvalidSpriteLoadNumber=14;
  80.       Err_NoPalette=15;
  81.       Err_PaletteWontFit=16;
  82.       Err_InvalidFade=17;
  83.  
  84. TYPE  FontChar=ARRAY[0..5] OF BYTE;
  85.       Font=ARRAY[0..255] OF Fontchar;
  86.       FontOrient=(horizontal,vertical);    {possible text directions       }
  87. CONST GraphTextOrientation:FontOrient=horizontal; {actual output direction }
  88.       GraphTextColor:BYTE=white;           {actual text color         }
  89.       GraphTextBackground:BYTE=white;
  90.       FontHeight=6;
  91.       FontWidth=6;
  92.  
  93. TYPE Table=ARRAY[0..NMAX] OF INTEGER;
  94.      ColorTable=ARRAY[0..255] OF BYTE;
  95.  
  96. TYPE PaletteEntry=RECORD red,green,blue:BYTE END;
  97.      Palette=ARRAY[0..255] OF PaletteEntry;
  98.      PalettePtr=^Palette;
  99.  
  100. CONST DefaultColors:Palette=       {default palette colors of 256color mode}
  101.  (                                 {read out by using the VGA-BIOS-calls:  }
  102.   (red:  0; green:  0; blue:  0),  { MOV AX,1017h ;read palette registers}
  103.   (red:  0; green:  0; blue: 42),  { XOR BX,BX    ;start with color 0 }
  104.   (red:  0; green: 42; blue:  0),  { MOV CX,100h  ;all 256 colors }
  105.   (red:  0; green: 42; blue: 42),  { LES DX,Ziel  ;to ES:DX   }
  106.   (red: 42; green:  0; blue:  0),  { INT 10h }
  107.   (red: 42; green:  0; blue: 42),  {Attention! These values can/could be   }
  108.   (red: 42; green: 21; blue:  0),  {read out directly from the VGA card not}
  109.   (red: 42; green: 42; blue: 42),  {until a graphic mode has been activated;  }
  110.   (red: 21; green: 21; blue: 21),  {thus, here they were defined "static"}
  111.   (red: 21; green: 21; blue: 63),
  112.   (red: 21; green: 63; blue: 21),
  113.   (red: 21; green: 63; blue: 63),
  114.   (red: 63; green: 21; blue: 21),
  115.   (red: 63; green: 21; blue: 63),
  116.   (red: 63; green: 63; blue: 21),
  117.   (red: 63; green: 63; blue: 63),
  118.   (red:  0; green:  0; blue:  0),
  119.   (red:  5; green:  5; blue:  5),
  120.   (red:  8; green:  8; blue:  8),
  121.   (red: 11; green: 11; blue: 11),
  122.   (red: 14; green: 14; blue: 14),
  123.   (red: 17; green: 17; blue: 17),
  124.   (red: 20; green: 20; blue: 20),
  125.   (red: 24; green: 24; blue: 24),
  126.   (red: 28; green: 28; blue: 28),
  127.   (red: 32; green: 32; blue: 32),
  128.   (red: 36; green: 36; blue: 36),
  129.   (red: 40; green: 40; blue: 40),
  130.   (red: 45; green: 45; blue: 45),
  131.   (red: 50; green: 50; blue: 50),
  132.   (red: 56; green: 56; blue: 56),
  133.   (red: 63; green: 63; blue: 63),
  134.   (red:  0; green:  0; blue: 63),
  135.   (red: 16; green:  0; blue: 63),
  136.   (red: 31; green:  0; blue: 63),
  137.   (red: 47; green:  0; blue: 63),
  138.   (red: 63; green:  0; blue: 63),
  139.   (red: 63; green:  0; blue: 47),
  140.   (red: 63; green:  0; blue: 31),
  141.   (red: 63; green:  0; blue: 16),
  142.   (red: 63; green:  0; blue:  0),
  143.   (red: 63; green: 16; blue:  0),
  144.   (red: 63; green: 31; blue:  0),
  145.   (red: 63; green: 47; blue:  0),
  146.   (red: 63; green: 63; blue:  0),
  147.   (red: 47; green: 63; blue:  0),
  148.   (red: 31; green: 63; blue:  0),
  149.   (red: 16; green: 63; blue:  0),
  150.   (red:  0; green: 63; blue:  0),
  151.   (red:  0; green: 63; blue: 16),
  152.   (red:  0; green: 63; blue: 31),
  153.   (red:  0; green: 63; blue: 47),
  154.   (red:  0; green: 63; blue: 63),
  155.   (red:  0; green: 47; blue: 63),
  156.   (red:  0; green: 31; blue: 63),
  157.   (red:  0; green: 16; blue: 63),
  158.   (red: 31; green: 31; blue: 63),
  159.   (red: 39; green: 31; blue: 63),
  160.   (red: 47; green: 31; blue: 63),
  161.   (red: 55; green: 31; blue: 63),
  162.   (red: 63; green: 31; blue: 63),
  163.   (red: 63; green: 31; blue: 55),
  164.   (red: 63; green: 31; blue: 47),
  165.   (red: 63; green: 31; blue: 39),
  166.   (red: 63; green: 31; blue: 31),
  167.   (red: 63; green: 39; blue: 31),
  168.   (red: 63; green: 47; blue: 31),
  169.   (red: 63; green: 55; blue: 31),
  170.   (red: 63; green: 63; blue: 31),
  171.   (red: 55; green: 63; blue: 31),
  172.   (red: 47; green: 63; blue: 31),
  173.   (red: 39; green: 63; blue: 31),
  174.   (red: 31; green: 63; blue: 31),
  175.   (red: 31; green: 63; blue: 39),
  176.   (red: 31; green: 63; blue: 47),
  177.   (red: 31; green: 63; blue: 55),
  178.   (red: 31; green: 63; blue: 63),
  179.   (red: 31; green: 55; blue: 63),
  180.   (red: 31; green: 47; blue: 63),
  181.   (red: 31; green: 39; blue: 63),
  182.   (red: 45; green: 45; blue: 63),
  183.   (red: 49; green: 45; blue: 63),
  184.   (red: 54; green: 45; blue: 63),
  185.   (red: 58; green: 45; blue: 63),
  186.   (red: 63; green: 45; blue: 63),
  187.   (red: 63; green: 45; blue: 58),
  188.   (red: 63; green: 45; blue: 54),
  189.   (red: 63; green: 45; blue: 49),
  190.   (red: 63; green: 45; blue: 45),
  191.   (red: 63; green: 49; blue: 45),
  192.   (red: 63; green: 54; blue: 45),
  193.   (red: 63; green: 58; blue: 45),
  194.   (red: 63; green: 63; blue: 45),
  195.   (red: 58; green: 63; blue: 45),
  196.   (red: 54; green: 63; blue: 45),
  197.   (red: 49; green: 63; blue: 45),
  198.   (red: 45; green: 63; blue: 45),
  199.   (red: 45; green: 63; blue: 49),
  200.   (red: 45; green: 63; blue: 54),
  201.   (red: 45; green: 63; blue: 58),
  202.   (red: 45; green: 63; blue: 63),
  203.   (red: 45; green: 58; blue: 63),
  204.   (red: 45; green: 54; blue: 63),
  205.   (red: 45; green: 49; blue: 63),
  206.   (red:  0; green:  0; blue: 28),
  207.   (red:  7; green:  0; blue: 28),
  208.   (red: 14; green:  0; blue: 28),
  209.   (red: 21; green:  0; blue: 28),
  210.   (red: 28; green:  0; blue: 28),
  211.   (red: 28; green:  0; blue: 21),
  212.   (red: 28; green:  0; blue: 14),
  213.   (red: 28; green:  0; blue:  7),
  214.   (red: 28; green:  0; blue:  0),
  215.   (red: 28; green:  7; blue:  0),
  216.   (red: 28; green: 14; blue:  0),
  217.   (red: 28; green: 21; blue:  0),
  218.   (red: 28; green: 28; blue:  0),
  219.   (red: 21; green: 28; blue:  0),
  220.   (red: 14; green: 28; blue:  0),
  221.   (red:  7; green: 28; blue:  0),
  222.   (red:  0; green: 28; blue:  0),
  223.   (red:  0; green: 28; blue:  7),
  224.   (red:  0; green: 28; blue: 14),
  225.   (red:  0; green: 28; blue: 21),
  226.   (red:  0; green: 28; blue: 28),
  227.   (red:  0; green: 21; blue: 28),
  228.   (red:  0; green: 14; blue: 28),
  229.   (red:  0; green:  7; blue: 28),
  230.   (red: 14; green: 14; blue: 28),
  231.   (red: 17; green: 14; blue: 28),
  232.   (red: 21; green: 14; blue: 28),
  233.   (red: 24; green: 14; blue: 28),
  234.   (red: 28; green: 14; blue: 28),
  235.   (red: 28; green: 14; blue: 24),
  236.   (red: 28; green: 14; blue: 21),
  237.   (red: 28; green: 14; blue: 17),
  238.   (red: 28; green: 14; blue: 14),
  239.   (red: 28; green: 17; blue: 14),
  240.   (red: 28; green: 21; blue: 14),
  241.   (red: 28; green: 24; blue: 14),
  242.   (red: 28; green: 28; blue: 14),
  243.   (red: 24; green: 28; blue: 14),
  244.   (red: 21; green: 28; blue: 14),
  245.   (red: 17; green: 28; blue: 14),
  246.   (red: 14; green: 28; blue: 14),
  247.   (red: 14; green: 28; blue: 17),
  248.   (red: 14; green: 28; blue: 21),
  249.   (red: 14; green: 28; blue: 24),
  250.   (red: 14; green: 28; blue: 28),
  251.   (red: 14; green: 24; blue: 28),
  252.   (red: 14; green: 21; blue: 28),
  253.   (red: 14; green: 17; blue: 28),
  254.   (red: 20; green: 20; blue: 28),
  255.   (red: 22; green: 20; blue: 28),
  256.   (red: 24; green: 20; blue: 28),
  257.   (red: 26; green: 20; blue: 28),
  258.   (red: 28; green: 20; blue: 28),
  259.   (red: 28; green: 20; blue: 26),
  260.   (red: 28; green: 20; blue: 24),
  261.   (red: 28; green: 20; blue: 22),
  262.   (red: 28; green: 20; blue: 20),
  263.   (red: 28; green: 22; blue: 20),
  264.   (red: 28; green: 24; blue: 20),
  265.   (red: 28; green: 26; blue: 20),
  266.   (red: 28; green: 28; blue: 20),
  267.   (red: 26; green: 28; blue: 20),
  268.   (red: 24; green: 28; blue: 20),
  269.   (red: 22; green: 28; blue: 20),
  270.   (red: 20; green: 28; blue: 20),
  271.   (red: 20; green: 28; blue: 22),
  272.   (red: 20; green: 28; blue: 24),
  273.   (red: 20; green: 28; blue: 26),
  274.   (red: 20; green: 28; blue: 28),
  275.   (red: 20; green: 26; blue: 28),
  276.   (red: 20; green: 24; blue: 28),
  277.   (red: 20; green: 22; blue: 28),
  278.   (red:  0; green:  0; blue: 16),
  279.   (red:  4; green:  0; blue: 16),
  280.   (red:  8; green:  0; blue: 16),
  281.   (red: 12; green:  0; blue: 16),
  282.   (red: 16; green:  0; blue: 16),
  283.   (red: 16; green:  0; blue: 12),
  284.   (red: 16; green:  0; blue:  8),
  285.   (red: 16; green:  0; blue:  4),
  286.   (red: 16; green:  0; blue:  0),
  287.   (red: 16; green:  4; blue:  0),
  288.   (red: 16; green:  8; blue:  0),
  289.   (red: 16; green: 12; blue:  0),
  290.   (red: 16; green: 16; blue:  0),
  291.   (red: 12; green: 16; blue:  0),
  292.   (red:  8; green: 16; blue:  0),
  293.   (red:  4; green: 16; blue:  0),
  294.   (red:  0; green: 16; blue:  0),
  295.   (red:  0; green: 16; blue:  4),
  296.   (red:  0; green: 16; blue:  8),
  297.   (red:  0; green: 16; blue: 12),
  298.   (red:  0; green: 16; blue: 16),
  299.   (red:  0; green: 12; blue: 16),
  300.   (red:  0; green:  8; blue: 16),
  301.   (red:  0; green:  4; blue: 16),
  302.   (red:  8; green:  8; blue: 16),
  303.   (red: 10; green:  8; blue: 16),
  304.   (red: 12; green:  8; blue: 16),
  305.   (red: 14; green:  8; blue: 16),
  306.   (red: 16; green:  8; blue: 16),
  307.   (red: 16; green:  8; blue: 14),
  308.   (red: 16; green:  8; blue: 12),
  309.   (red: 16; green:  8; blue: 10),
  310.   (red: 16; green:  8; blue:  8),
  311.   (red: 16; green: 10; blue:  8),
  312.   (red: 16; green: 12; blue:  8),
  313.   (red: 16; green: 14; blue:  8),
  314.   (red: 16; green: 16; blue:  8),
  315.   (red: 14; green: 16; blue:  8),
  316.   (red: 12; green: 16; blue:  8),
  317.   (red: 10; green: 16; blue:  8),
  318.   (red:  8; green: 16; blue:  8),
  319.   (red:  8; green: 16; blue: 10),
  320.   (red:  8; green: 16; blue: 12),
  321.   (red:  8; green: 16; blue: 14),
  322.   (red:  8; green: 16; blue: 16),
  323.   (red:  8; green: 14; blue: 16),
  324.   (red:  8; green: 12; blue: 16),
  325.   (red:  8; green: 10; blue: 16),
  326.   (red: 11; green: 11; blue: 16),
  327.   (red: 12; green: 11; blue: 16),
  328.   (red: 13; green: 11; blue: 16),
  329.   (red: 15; green: 11; blue: 16),
  330.   (red: 16; green: 11; blue: 16),
  331.   (red: 16; green: 11; blue: 15),
  332.   (red: 16; green: 11; blue: 13),
  333.   (red: 16; green: 11; blue: 12),
  334.   (red: 16; green: 11; blue: 11),
  335.   (red: 16; green: 12; blue: 11),
  336.   (red: 16; green: 13; blue: 11),
  337.   (red: 16; green: 15; blue: 11),
  338.   (red: 16; green: 16; blue: 11),
  339.   (red: 15; green: 16; blue: 11),
  340.   (red: 13; green: 16; blue: 11),
  341.   (red: 12; green: 16; blue: 11),
  342.   (red: 11; green: 16; blue: 11),
  343.   (red: 11; green: 16; blue: 12),
  344.   (red: 11; green: 16; blue: 13),
  345.   (red: 11; green: 16; blue: 15),
  346.   (red: 11; green: 16; blue: 16),
  347.   (red: 11; green: 15; blue: 16),
  348.   (red: 11; green: 13; blue: 16),
  349.   (red: 11; green: 12; blue: 16),
  350.   (red:  0; green:  0; blue:  0),
  351.   (red:  0; green:  0; blue:  0),
  352.   (red:  0; green:  0; blue:  0),
  353.   (red:  0; green:  0; blue:  0),
  354.   (red:  0; green:  0; blue:  0),
  355.   (red:  0; green:  0; blue:  0),
  356.   (red:  0; green:  0; blue:  0),
  357.   (red:  0; green:  0; blue:  0)
  358.  );
  359.  
  360. VAR Error:BYTE; {global error variable }
  361.     SpriteN:Table;
  362.     SpriteX:Table;
  363.     SpriteY:Table;
  364.     NextSprite:ARRAY[0..LoadMAX] OF WORD;
  365.     SPRITEAD:ARRAY[0..LoadMAX] OF WORD;
  366.     PAGE,PAGEADR,SCROLLADR,BACKGNDADR:WORD;
  367.     Color:BYTE;           {drawing color for lines }
  368.     ActualColors:Palette; {actual color palette}
  369.     was_cut:BOOLEAN;      {TRUE/FALSE, if "GetImage" had to clip image  }
  370.     left_cut,             {variables set by "GetImage"; if "was_cut" has}
  371.     right_cut,            {been set to TRUE, they will report, where and  }
  372.     top_cut,              {and how much of the image had to been clipped }
  373.     bottom_cut:WORD;      {off                                          }
  374.  
  375.     BackgroundMode:BYTE;
  376.     BackTile:ARRAY[0..MaxTiles] OF BYTE;    {tile memory    }
  377.     XTiles,YTiles:INTEGER;                  {width, height of defined area  }
  378.     BackX1,BackY1,BackX2,BackY2:INTEGER;    {coordinates of the defined area }
  379.  
  380. CONST   Fade_Squares =0;
  381.         Fade_Moiree1 =1;
  382.         Fade_Moiree2 =2;
  383.         Fade_Moiree3 =3;
  384.         Fade_Moiree4 =4;
  385.         Fade_Moiree5 =5;
  386.         Fade_Moiree6 =6;
  387.         Fade_Moiree7 =7;
  388.         Fade_Moiree8 =8;
  389.         Fade_Moiree9 =9;
  390.         Fade_Moiree10=10;
  391.         Fade_Moiree11=11;
  392.         Fade_Moiree12=12;
  393.         Fade_Moiree13=13;
  394.         Fade_Moiree14=14;
  395.         Fade_SweepInFromTop=15;
  396.         Fade_SweepInFromBottom=16;
  397.         Fade_SweepInFromLeft=17;
  398.         Fade_SweepInFromRight=18;
  399.         Fade_ScrollInFromTop=19;
  400.         Fade_ScrollInFromBottom=20;
  401.         Fade_ScrollInFromLeft=21;
  402.         Fade_ScrollInFromRight=22;
  403.         Fade_Circles =23;
  404.  
  405.  PROCEDURE ShadowTab;
  406.  PROCEDURE SetShadowTab(brightness:BYTE);
  407.  PROCEDURE SetPalette(pal:Palette; update:BOOLEAN);
  408.  PROCEDURE GetPalette(VAR pal:Palette);
  409.  FUNCTION LoadPalette(name:String; number:BYTE; VAR pal:Palette):WORD;
  410.  PROCEDURE SetCycleTime(milliseconds:WORD);
  411.  PROCEDURE SetSpriteCycle(nr,len:WORD);
  412.  FUNCTION GetImage(x1,y1,x2,y2:INTEGER; pa:BYTE):POINTER;
  413.  PROCEDURE PutImage(x,y:INTEGER; p:POINTER; pa:BYTE);
  414.  PROCEDURE FreeImageMem(p:POINTER);
  415.  PROCEDURE InitGraph;
  416.  PROCEDURE Screen(pa:BYTE);
  417.  PROCEDURE Line(x1,y1,x2,y2:INTEGER; pa:BYTE);
  418.  PROCEDURE BackgroundLine(x1,y1,x2,y2:INTEGER);
  419.  FUNCTION GetPixel(x,y:INTEGER):BYTE;
  420.  FUNCTION BackgroundGetPixel(x,y:INTEGER):BYTE;
  421.  FUNCTION PageGetPixel(x,y:INTEGER; pa:BYTE):BYTE;
  422.  PROCEDURE PutPixel(x,y:INTEGER; color:Byte);
  423.  PROCEDURE BackgroundPutPixel(x,y:INTEGER; color:Byte);
  424.  PROCEDURE PagePutPixel(x,y:INTEGER; color,pa:Byte);
  425.  PROCEDURE OutTextXY(x,y:INTEGER; pa:BYTE; s:STRING);
  426.  PROCEDURE BackgroundOutTextXY(x,y:INTEGER; s:STRING);
  427.  FUNCTION Hitdetect(s1,s2:INTEGER):BOOLEAN;
  428.  PROCEDURE Animate;
  429.  FUNCTION LoadSprite(name:String; number:WORD):WORD;
  430.  FUNCTION LoadTile(name:STRING; number:BYTE):WORD;
  431.  PROCEDURE SetBackgroundScrollRange(x1,y1,x2,y2:INTEGER);
  432.  PROCEDURE SetBackgroundMode(mode:BYTE);
  433.  PROCEDURE PutTile(x,y:INTEGER; TileNr:BYTE);
  434.  PROCEDURE SetOffscreenTile(TileNr:BYTE);
  435.  PROCEDURE SetModeByte(Sp:WORD; M:BYTE);
  436.  FUNCTION GetModeByte(Sp:WORD):BYTE;
  437.  PROCEDURE FillPage(pa,color:Byte);
  438.  PROCEDURE FillBackground(color:BYTE);
  439.  PROCEDURE GetBackgroundFromPage(pa:Byte);
  440.  PROCEDURE WritePage(name:STRING; pa:BYTE);
  441.  PROCEDURE LoadPage(name:STRING; pa:BYTE);
  442.  PROCEDURE WriteBackgroundPage(name:STRING);
  443.  PROCEDURE LoadBackgroundPage(name:STRING);
  444.  PROCEDURE FadeIn(pa,ti,style:WORD);
  445.  PROCEDURE InitRoutines;
  446.  PROCEDURE CloseRoutines;
  447.  FUNCTION GetErrorMessage:STRING;
  448.  
  449.  
  450. {--------------------------------------------------------------------------}
  451.  
  452. IMPLEMENTATION
  453.  
  454. CONST StartIndex=0;
  455.       EndIndex=StartIndex+3;
  456.       {offset addresses of the graphic pages (in segment $A000):}
  457.       Offset_Adr:Array[StartIndex..EndIndex] OF WORD=($0000,$3E80,$7D00,$BB80);
  458.       {segment addresses of graphic pages (with offset = 0):}
  459.       Segment_Adr:ARRAY[StartIndex..EndIndex] OF WORD=($A000,$A3E8,$A7D0,$ABB8);
  460.  
  461.       {sprite header format:}
  462.  
  463. {      0..1   DW offset_ptr_to_plane0_data}
  464. {      2..3   DW offset_ptr_to_plane1_data}
  465. {      4..5   DW offset_ptr_to_plane2_data}
  466. {      6..7   DW offset_ptr_to_plane3_data}
  467. {      8..9   DW width (in groups of 4)     (in German: "Breite")}
  468. {      10..11 DW heigth in rows             (in German: "Hoehe")}
  469. {      12..15 DB 1,2,4,8      ; translate-table used for port-accesses }
  470. {      16..17 DW SpriteLength ; length of this sprite in bytes}
  471. {                             ; now follows the scratch area reserved for temporary}
  472. {                             ; variables: ("a|b" = used for a and b)}
  473. {      18..19 DW ?            ; licutoff_      | hit1xfirst}
  474. {      20..21 DW ?            ; zeilenadr      | hit1yfirst}
  475. {      22..23 DW ?            ; bildx          | hit2xfirst}
  476. {      24..25 DW ?            ; yoffset_       | hit2yfirst}
  477. {      26..27 DW ?            ; end_min_start  | ueberlappx_1}
  478. {      28..29 DW ?            ;                | ueberlappy_1}
  479. {      30..31 DW ?            ;                | x1}
  480. {      32..33 DW ?            ;                | x2}
  481. {      34..35 DW ?            ;                | y1}
  482. {      36..37 DW ?            ;                | y2}
  483. {      38..39 DB 'K','R'      ; tag used for sprites}
  484. {      40     DB 1            ; version number}
  485. {      41     DB 0            ; display mode used for sprite}
  486. {      42..43 DW offset_ptr_to_left_boundary_data}
  487. {      44..45 DW offset_ptr_to_right_boundary_data}
  488. {      46..47 DW offset_ptr_to_top_boundary_data}
  489. {      48..49 DW offset_ptr_to_bottom_boundary_data}
  490. {      50..?? DB data }
  491.  
  492. {      for ex.:   xxrxxxxx, with: r=red=40, g=green=45, b=blue=35, x=white=30}
  493. {                 xrgrxxxx}
  494. {                 rbgbrxxx}
  495. {      }
  496.  
  497.       {addresses of important values inside the sprite header:   }
  498.       Left=42;
  499.       Right=44;
  500.       Top=46;
  501.       Bottom=48;
  502.       Breite=8;
  503.       Hoehe=10;
  504.       Translate=12;
  505.       SpriteLength=16;
  506.       Kennung=38;
  507.       Version=40;
  508.       Modus=41;
  509.  
  510.       {addresses of temporary variables for the drawing routines:  }
  511.       licutoff_=18;
  512.       zeilenadr=20;
  513.       bildx=22;
  514.       yoffset_=24;
  515.       end_min_start=26;
  516.  
  517.       {addresses of temporary variables for the collision detection routine:}
  518.       hit1xfirst=18;
  519.       hit1yfirst=20;
  520.       hit2xfirst=22;
  521.       hit2yfirst=24;
  522.       ueberlappx_1=26;
  523.       ueberlappy_1=28;
  524.       x1=30;
  525.       x2=32;
  526.       y1=34;
  527.       y2=36;
  528.  
  529.       TranslateTab:ARRAY[0..3] OF BYTE=(1,2,4,8); {For mask addressing    }
  530.       PICHeader:STRING[3]='PIC'; {tag in picture files    }
  531.       Schatten :BYTE=70;         {default brightness of shadows  }
  532.  
  533. TYPE SpriteHeader= RECORD
  534.                     Zeiger_auf_Plane:Array[0..3] OF Word;
  535.                     Breite_in_4er_Gruppen:WORD;
  536.                     Hoehe_in_Zeilen:WORD;
  537.                     Translate:Array[1..4] OF Byte;
  538.                     SpriteLength:WORD;
  539.                     Dummy:Array[1..10] OF Word;
  540.                     Kennung:ARRAY[1..2] OF CHAR;
  541.                     Version:BYTE;
  542.                     Modus:BYTE;
  543.                     ZeigerL,ZeigerR,ZeigerO,ZeigerU:Word;
  544.                    END;
  545.  
  546. CONST FontMask:ARRAY[0..7] OF BYTE=($80,$40,$20,$10,8,4,2,1);
  547.       FontData: Font=(                   {used font:                }
  548.       (  0,  0,  0,  0,  0,  0), {#0}    {selfmade 6x6 font (ugly..)}
  549.       (  0,216,  0,248,112,  0), {#1}
  550.       (248,168,248,136,216,248), {#2}
  551.       (  0, 80,248,248,112, 32), {#3}
  552.       (  0, 32,112,248,112, 32), {#4}
  553.       ( 32,112,216,248, 32,112), {#5}
  554.       ( 32,112,248,248, 32,112), {#6}
  555.       (  0, 32,216,216, 32,  0), {#7}
  556.       (248,216,168,168,216,248), {#8}
  557.       (  0,112,200,152,112,  0), {#9}
  558.       (248,136,168,168,136,248), {#10}
  559.       ( 56, 24, 32,112,136,112), {#11}
  560.       (112,136,112, 32,248, 32), {#12}
  561.       ( 56, 40, 32, 32,224,224), {#13}
  562.       (  0,120, 72,120, 72,216), {#14}
  563.       (  0, 32,168, 80,168, 32), {#15}
  564.       (  0,128,224,248,224,128), {#16}
  565.       (  0,  8, 56,248, 56,  8), {#17}
  566.       ( 32,112,168,168,112, 32), {#18}
  567.       (  0,216,216,216,  0,216), {#19}
  568.       (  0,120,168,104, 40, 40), {#20}
  569.       ( 24, 32, 16, 40,144, 96), {#21}
  570.       (  0,  0,  0,  0,248,248), {#22}
  571.       ( 32,112, 32,112, 32,248), {#23}
  572.       (  0, 32,112,248, 32, 32), {#24}
  573.       (  0, 32, 32,248,112, 32), {#25}
  574.       (  0, 32, 16,248, 16, 32), {#26}
  575.       (  0, 32, 64,248, 64, 32), {#27}
  576.       (  0,  0,192,248,  0,  0), {#28}
  577.       (  0,  0, 80,248, 80,  0), {#29}
  578.       (  0,  0,  0, 32,112,248), {#30}
  579.       (  0,  0,248,112, 32,  0), {#31}
  580.       (  0,  0,  0,  0,  0,  0), { }
  581.       (  0, 48, 48, 48,  0, 48), {!}
  582.       (  0, 80, 80,  0,  0,  0), {"}
  583.       (  0, 80,248, 80,248, 80), {#}
  584.       ( 32,120,160,112, 40,240), { $}
  585.       (  0,200, 16, 32, 64,152), {%}
  586.       (  0,112,216,112,152,104), {&}
  587.       (  0, 16, 32,  0,  0,  0), {'}
  588.       (  0,112,192,192,192,112), {(}
  589.       (  0,224, 48, 48, 48,224), {)}
  590.       (  0, 80, 32,248, 32, 80), {*}
  591.       (  0,  0, 32,248, 32,  0), {+}
  592.       (  0,  0,  0, 32, 32, 64), {,}
  593.       (  0,  0,  0,248,  0,  0), {-}
  594.       (  0,  0,  0,  0, 48,  0), {.}
  595.       (  4,  8, 16, 32, 64,128), {/}
  596.       (  0,112,152,168,200,112), {0}
  597.       (  0, 48,112, 48, 48,120), {1}
  598.       (  0,240, 24,112,192,248), {2}
  599.       (  0,240, 24,112, 24,240), {3}
  600.       (  0,192,208,248, 48, 48), {4}
  601.       (  0,248,192,240, 24,240), {5}
  602.       (  0,248,128,248,136,248), {6}
  603.       (  0,248, 24, 48, 96, 96), {7}
  604.       (  0,112,216,112,216,112), {8}
  605.       (  0,112,136,120,  8,112), {9}
  606.       (  0,  0, 32,  0, 32,  0), {:}
  607.       (  0,  0, 32,  0, 32, 64), {;}
  608.       (  0, 24, 48, 96, 48, 24), {<}
  609.       (  0,  0,248,  0,248,  0), {=}
  610.       (  0, 96, 48, 24, 48, 96), {>}
  611.       (112,136, 16, 32,  0, 32), {?}
  612.       (  0,112,136,184,128,120), {@}
  613.       (  0,112,200,248,200,200), {A}
  614.       (  0,240,200,240,200,240), {B}
  615.       (  0,120,192,192,192,120), {C}
  616.       (  0,240,216,200,216,240), {D}
  617.       (  0,248,192,240,192,248), {E}
  618.       (  0,248,192,240,192,192), {F}
  619.       (  0,120,192,216,200,120), {G}
  620.       (  0,200,200,248,200,200), {H}
  621.       (  0,120, 48, 48, 48,120), {I}
  622.       (  0,248,  8,  8,200,112), {J}
  623.       (  0,200,208,224,208,200), {K}
  624.       (  0,192,192,192,192,248), {L}
  625.       (  0,136,216,168,136,136), {M}
  626.       (  0,136,200,168,152,136), {N}
  627.       (  0,112,200,200,200,112), {O}
  628.       (  0,240,200,240,192,192), {P}
  629.       (  0, 96,208,208,208,104), {Q}
  630.       (  0,240,136,240,208,200), {R}
  631.       (  0,248,192,248, 24,248), {S}
  632.       (  0,248, 96, 96, 96, 96), {T}
  633.       (  0,200,200,200,200,248), {U}
  634.       (  0,200,200,200,200, 48), {V}
  635.       (  0,136,136,168,248, 80), {W}
  636.       (  0,136,216,112,216,136), {X}
  637.       (  0,200,200,112, 48, 48), {Y}
  638.       (  0,248, 24,112,192,248), {Z}
  639.       (  0,120, 96, 96, 96,120), {[}
  640.       (128, 64, 32, 16,  8,  4), {\}
  641.       (  0,120, 24, 24, 24,120), {]}
  642.       ( 32, 80,136,  0,  0,  0), {^}
  643.       (  0,  0,  0,  0,  0,248), {_}
  644.       ( 64, 32,  0,  0,  0,  0), {`}
  645.       (  0,  0,112,200,200,120), {a}
  646.       (  0,128,240,136,136,240), {b}
  647.       (  0,  0,120,192,192,120), {c}
  648.       (  0,  8,120,136,136,120), {d}
  649.       (  0,  0,112,248,128,112), {e}
  650.       (  0, 24, 32,120, 32, 32), {f}
  651.       (  0,112,136,120,  8,112), {g}
  652.       (  0,192,240,200,200,200), {h}
  653.       ( 48,  0, 48, 48, 48, 48), {i}
  654.       ( 24,  0, 24, 24,216,112), {j}
  655.       (  0,192,208,224,216,216), {k}
  656.       (  0, 96, 96, 96, 96, 56), {l}
  657.       (  0,  0,208,248,168,136), {m}
  658.       (  0,  0,240,200,200,200), {n}
  659.       (  0,  0,112,200,200,112), {o}
  660.       (  0,  0,240,200,240,192), {p}
  661.       (  0,  0,112,152,120, 24), {q}
  662.       (  0,  0,176,104, 96, 96), {r}
  663.       (  0, 56, 64, 48,136,112), {s}
  664.       (  0, 96,248, 96,104, 48), {t}
  665.       (  0,  0,200,200,200,120), {u}
  666.       (  0,  0,200,200,200,112), {v}
  667.       (  0,  0,136,168,168,112), {w}
  668.       (  0,  0,216, 96, 48,216), {x}
  669.       (  0,  0,200,248,  8,112), {y}
  670.       (  0,  0,240, 48,192,248), {z}
  671.       (  0, 56, 96,192, 96, 56),(*{*)
  672.       (  0, 16, 16,  0, 16, 16), {|}
  673.       (  0,224, 48, 24, 48,224),(*}*)
  674.       (  0,104,144,  0,  0,  0), {~}
  675.       (  0, 32, 80,136,248,  0), {#127}
  676.       (112,200,128,200,112,192), {#128}
  677.       (  0,200,  0,200,200,120), {#129}
  678.       ( 24, 32,112,248,128,112), {#130}
  679.       ( 16, 40,  0,120,196,124), {#131}
  680.       (104,  0,112,200,200,120), {#132}
  681.       ( 48,  8,112,136,136,120), {#133}
  682.       ( 16, 40, 16,112,200,120), {#134}
  683.       (  0,120,192,120, 16, 96), {#135}
  684.       (112,  0,112,248,192,112), {#136}
  685.       ( 80,  0,112,248,128,112), {#137}
  686.       ( 48,  8,112,248,192,112), {#138}
  687.       (104,  0, 48, 48, 48, 48), {#139}
  688.       ( 48, 72,  0, 48, 48, 48), {#140}
  689.       ( 96, 16,  0, 48, 48, 48), {#141}
  690.       (200,  0,112,200,248,200), {#142}
  691.       ( 48,  0,112,200,248,200), {#143}
  692.       (112,248,192,240,192,248), {#144}
  693.       (  0,208, 40,112,160, 88), {#145}
  694.       (  0, 56, 80,248,144,152), {#146}
  695.       ( 32, 80,  0,112,200,112), {#147}
  696.       ( 80,  0,112,200,200,112), {#148}
  697.       ( 96, 16,  0,112,200,112), {#149}
  698.       ( 32, 80,  0,200,200,120), {#150}
  699.       ( 96, 16,  0,200,200,120), {#151}
  700.       ( 80,  0,200,248,  8,112), {#152}
  701.       ( 80,  0,112,200,200,112), {#153}
  702.       (200,  0,200,200,200,248), {#154}
  703.       ( 16,120,128,128,120, 16), {#155}
  704.       ( 48, 72,224, 64,136,248), {#156}
  705.       (216, 32,248, 32,248, 32), {#157}
  706.       (192,160,208,184,144,152), {#158}
  707.       ( 48, 40, 96, 48,160, 96), {#159}
  708.       ( 48, 64,  0,112,136,120), {#160}
  709.       ( 48, 64,  0, 32, 32, 32), {#161}
  710.       ( 48, 64,  0,112,200,112), {#162}
  711.       ( 48, 64,  0,200,200,120), {#163}
  712.       (104,144,  0,176, 72, 72), {#164}
  713.       (104,144,  0,200,168,152), {#165}
  714.       (112,144,104,  0,248,  0), {#166}
  715.       (112,136,112,  0,248,  0), {#167}
  716.       ( 32,  0, 32, 64,136,112), {#168}
  717.       (  0,  0,252,192,  0,  0), {#169}
  718.       (  0,  0,252, 12,  0,  0), {#170}
  719.       ( 72, 80, 32, 64,168, 40), {#171}
  720.       ( 72, 80, 32, 80,152,  8), {#172}
  721.       ( 48,  0, 48, 48, 48,  0), {#173}
  722.       ( 40, 80,160, 80, 40,  0), {#174}
  723.       (160, 80, 40, 80,160,  0), {#175}
  724.       ( 84,168, 84,168, 84,168), {#176}
  725.       (252,252,252,252,252,252), {#177}
  726.       (168, 84,168, 84,168, 84), {#178}
  727.       ( 16, 16, 16, 16, 16, 16), {#179}
  728.       ( 16, 16, 16,240, 16, 16), {#180}
  729.       ( 16, 16,240, 16,240, 16), {#181}
  730.       ( 40, 40, 40,232, 40, 40), {#182}
  731.       (  0,  0,  0,248, 40, 40), {#183}
  732.       (  0,  0,240, 16,240, 16), {#184}
  733.       ( 40, 40,232,  8,232, 40), {#185}
  734.       ( 40, 40, 40, 40, 40, 40), {#186}
  735.       (  0,  0,248,  8,232, 40), {#187}
  736.       ( 40, 40,232,  8,248,  0), {#188}
  737.       ( 40, 40, 40,248,  0,  0), {#189}
  738.       ( 16, 16,240, 16,240,  0), {#190}
  739.       (  0,  0,  0,240, 16, 16), {#191}
  740.       ( 16, 16, 16, 28,  0,  0), {#192}
  741.       ( 16, 16, 16,252,  0,  0), {#193}
  742.       (  0,  0,  0,252, 16, 16), {#194}
  743.       ( 16, 16, 16, 28, 16, 16), {#195}
  744.       (  0,  0,  0,252,  0,  0), {#196}
  745.       ( 16, 16, 16,252, 16, 16), {#197}
  746.       ( 16, 16, 28, 16, 28, 16), {#198}
  747.       ( 40, 40, 40, 44, 40, 40), {#199}
  748.       ( 40, 40, 44, 32, 60,  0), {#200}
  749.       (  0,  0, 60, 32, 44, 40), {#201}
  750.       ( 40, 40,236,  0,252,  0), {#202}
  751.       (  0,  0,252,  0,236, 40), {#203}
  752.       ( 40, 40, 44, 32, 44, 40), {#204}
  753.       (  0,  0,252,  0,252,  0), {#205}
  754.       ( 40, 40,236,  0,236, 40), {#206}
  755.       ( 16, 16,252,  0,252,  0), {#207}
  756.       ( 40, 40, 40,252,  0,  0), {#208}
  757.       (  0,  0,252,  0,252, 16), {#209}
  758.       (  0,  0,  0,252, 40, 40), {#210}
  759.       ( 40, 40, 40, 60,  0,  0), {#211}
  760.       ( 16, 16, 28, 16, 28,  0), {#212}
  761.       (  0,  0, 28, 16, 28, 16), {#213}
  762.       (  0,  0,  0, 60, 40, 40), {#214}
  763.       ( 40, 40, 40,252, 40, 40), {#215}
  764.       ( 16, 16,252, 16,252, 16), {#216}
  765.       ( 16, 16, 16,240,  0,  0), {#217}
  766.       (  0,  0, 28, 16, 16, 16), {#218}
  767.       (252,252,252,252,252,252), {#219}
  768.       (  0,  0,  0,252,252,252), {#220}
  769.       (192,192,192,192,192,192), {#221}
  770.       ( 12, 12, 12, 12, 12, 12), {#222}
  771.       (252,252,252,  0,  0,  0), {#223}
  772.       (  0,  0,104,144,144,104), {#224}
  773.       (  0,112,152,176,136,176), {#225}
  774.       (  0,248,136,128,128,128), {#226}
  775.       (  0,  0,248, 80, 80, 80), {#227}
  776.       (248, 72, 32, 64,136,248), {#228}
  777.       (  0,  0,120,144,144, 96), {#229}
  778.       (  0, 72, 72,120, 64,192), {#230}
  779.       (  0,  0,104,176, 32, 32), {#231}
  780.       (  0,248, 32, 80, 32,248), {#232}
  781.       (  0,112,136,248,136,112), {#233}
  782.       (  0,112,136,136, 80,216), {#234}
  783.       ( 56, 64, 32,112,136,112), {#235}
  784.       (  0,  0, 80,168, 80,  0), {#236}
  785.       (  0,  8, 80,168, 80,128), {#237}
  786.       (  0,120,128,248,128,120), {#238}
  787.       (  0,  0,112,136,136,136), {#239}
  788.       (  0,248,  0,248,  0,248), {#240}
  789.       (  0, 32,112, 32,  0,248), {#241}
  790.       ( 64, 32, 16, 32, 64,248), {#242}
  791.       ( 16, 32, 64, 32, 16,248), {#243}
  792.       ( 16, 40, 32, 32, 32, 32), {#244}
  793.       ( 32, 32, 32, 32,160, 64), {#245}
  794.       (  0, 32,  0,248,  0, 32), {#246}
  795.       (104,144,  0,104,144,  0), {#247}
  796.       ( 96,144, 96,  0,  0,  0), {#248}
  797.       (  0,  0,  0, 48,  0,  0), {#249}
  798.       (  0,  0,  0, 16,  0,  0), {#250}
  799.       ( 60, 32, 32,160, 96, 32), {#251}
  800.       (176, 72, 72,  0,  0,  0), {#252}
  801.       (224, 16, 96,128,240,  0), {#253}
  802.       (  0,  0,112,112,  0,  0), {#254}
  803.       (  0,  0,  0,  0,  0,  0));{#255}
  804.  
  805. VAR Steigung:BYTE;        {determines, which algorithm will be used  }
  806.     DY_mal2,DY_m_DX_mal2:INTEGER;
  807.     oldMode:byte;
  808.     regs:registers;
  809.  
  810.     IsAT:BYTE;
  811.     TimeFlag:BYTE;
  812.     CycleTime:LONGINT;
  813.  
  814.     CRTAddress, StatusReg : WORD;
  815.  
  816.  
  817. {-----------------------------------------------------}
  818.  
  819. PROCEDURE ShadowTab; ASSEMBLER;
  820. {Pseudo-procedure to store the color lookup table into the code segment}
  821. {DO NOT TRY TO CALL THIS "PROCEDURE"!!!                                  }
  822. {default values correspond to a darkening to 70% of the original brightness}
  823. ASM
  824.    DB 254,104,120,124,112,108,114, 24, 20,128,144,  3,136,  5,140,  7
  825.    DB 254,254, 17, 17, 18, 19, 20, 20, 21,  8, 23, 24, 24, 25, 26,  7
  826.    DB   1,  1,107,108,  5,108,109,  4,  4,  4,  6,  6,116,116,117,  2
  827.    DB   2,  2,123,124,  3,124,125,  1,152,155,156,156,  5,156,156,157
  828.    DB 160,163,164,164,164,164,164,165,168,171,172,172,  3,172,172,173
  829.    DB  24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24
  830.    DB  24, 24, 24, 24, 24, 24, 24, 24,176,177,178,179,180,181,182,183
  831.    DB 184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199
  832.    DB 200,201,203,204,204,204,205,207,208,209,211,212,212,212,213,215
  833.    DB 216,217,219,220,220,220,221,223,246,227,228,228,228,228,228,229
  834.    DB 234,235,236,236,236,236,236,237,242,243,244,244,244,244,244,245
  835.    DB 254,254,254,254,254,254,254,254,254,254,254,254,254,254,254,254
  836.    DB 254,254,254,254,254,254,254,254, 17, 17, 17, 17, 17, 17, 17, 17
  837.    DB  17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17
  838.    DB  17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17
  839.    DB  17, 17, 17, 17, 17, 17, 17, 17,254,254,254,254,254,254,254,  7
  840. END;
  841.  
  842. PROCEDURE CS_TranslateTab; ASSEMBLER;
  843. {Small pseudo-procedure to store lookup table for the bitmasks into   }
  844. {the code segment, too}
  845. ASM
  846.  DB 1,2,4,8
  847. END;
  848.  
  849. PROCEDURE SetShadowTab(brightness:BYTE);
  850. { in: brightness = wanted brightness for the colors in the shadow area, }
  851. {                  in percentage of the brightness of the original colors    }
  852. {out: ShadowTab  = (best approximation) color table for wanted dimming  }
  853. {     Schatten   = new brightness (Schatten is a global variable!)      }
  854. {rem: default value of ShadowTab is 70% of the original color brightness!  }
  855. {     This routine takes some time (about 4 sec on a 8MHz-AT!)           }
  856.  
  857. VAR neue_Tabelle:ColorTable;
  858.     p1:POINTER;
  859.  
  860. BEGIN
  861.  IF (brightness<0) OR (brightness>100)
  862.   THEN BEGIN
  863.         Error:=Err_InvalidPercentage;
  864.         exit
  865.        END;
  866.  p1:=@neue_Tabelle; {trick, as the assembler messes up accesses to the SS-segment}
  867.  ASM
  868.    MOV CX,256 {outer loop-counter        }
  869.    LES DI,p1  {ES:DI=^neue_Tabelle[i] }
  870.    MOV SI,OFFSET ActualColors {DS:SI=^ActualColors[]}
  871.  
  872.  @outerloop:
  873.    LODSB          {AL=tempColors[i].red}
  874.    MUL brightness {will be addressed via SS!   }
  875.    MOV DL,100
  876.    DIV DL         {AL=tempColors[i].red*brightness DIV 100}
  877.    MOV BL,AL      {BL := AL = new red part   }
  878.  
  879.    LODSB          {dto., for green }
  880.    MUL brightness
  881.    MOV DL,100
  882.    DIV DL
  883.    MOV BH,AL      {...into BH}
  884.  
  885.    LODSB          {dto., for blue }
  886.    MUL brightness
  887.    MOV DL,100
  888.    DIV DL
  889.    MOV DH,AL      {...into DH}
  890.  
  891.      {BL/BH/DH = RGB-parts of the color, for which we are seeking an approximation}
  892.      PUSH CX
  893.      PUSH SI
  894.      PUSH DI
  895.      PUSH BP
  896.  
  897.      MOV DI,65535 {min. square of error (up to now)         }
  898.      MOV CX,256   {walk through all 256 default colors}
  899.      MOV SI,OFFSET ActualColors  {DS:SI=^ActualColors[]}
  900.     @searchloop:
  901.      MOV AL,BL    {compute difference in red part  }
  902.      SUB AL,[SI]
  903.      JL @noNewMin {new color may not be brighter!    }
  904.      MUL AL       {compute square of error}
  905.      MOV BP,AX
  906.  
  907.      MOV AL,BH    {dto., for green part  }
  908.      SUB AL,[SI+1]
  909.      JL @noNewMin
  910.      MUL AL
  911.      ADD BP,AX
  912.      JC @noNewMin {don't tolerate huge color differences       }
  913.  
  914.      MOV AL,DH    {dto., for blue part  }
  915.      SUB AL,[SI+2]
  916.      JL @noNewMin
  917.      MUL AL
  918.      ADD AX,BP
  919.      JC @noNewMin
  920.  
  921.      CMP AX,DI    {did we find a better approximation?}
  922.      JAE @noNewMin  {no  }
  923.      MOV DI,AX      {yes, store square of error and color}
  924.      MOV DL,CL
  925.      OR DI,DI       {square of error = 0?}
  926.      JZ @ColorDone  {yes, we can't find any better solution than that!   }
  927.  
  928.     @noNewMin:
  929.      ADD SI,3
  930.      LOOP @searchloop
  931.  
  932.        CMP DI,65535   {no color found?      }
  933.        JNE @ColorDone {but yes, nothing to do!}
  934.        MOV CX,256     {no, thus search again    }
  935.        MOV SI,OFFSET ActualColors  {DS:SI=^ActualColors[]}
  936.       @searchloop2:
  937.        LODSB
  938.        SUB AL,BL      {Diff ≈±2^6 -> square ≈±2^12 -> 3*square<MaxInt  }
  939.        IMUL AL        {so no overflow is possible }
  940.        MOV BP,AX
  941.  
  942.        LODSB          {dto., for green part  }
  943.        SUB AL,BH
  944.        IMUL AL
  945.        ADD BP,AX
  946.  
  947.        LODSB          {dto., for blue part  }
  948.        SUB AL,DH
  949.        IMUL AL
  950.        ADD AX,BP
  951.  
  952.        CMP AX,DI      {did we find a better approximation?}
  953.        JAE @noNewMin2 {no  }
  954.        MOV DI,AX      {yes, store square of error and color}
  955.        MOV DL,CL
  956.  
  957.       @noNewMin2:
  958.        LOOP @searchloop2
  959.  
  960.  
  961.     @ColorDone:     {100h-DL = optimal color found     }
  962.      POP BP
  963.      POP DI         {ES:DI=^neue_Tabelle[i] }
  964.      POP SI         {DS:SI=^ActualColors[i] }
  965.      POP CX
  966.  
  967.    MOV AL,DL      {store into neue_Tabelle[i]  }
  968.    NEG AL         {AL=100h-DL = best approximation}
  969.    STOSB
  970.  
  971.    DEC CX         {replacement for "LOOP @outerloop"; next color!}
  972.    JCXZ @fertig
  973.    JMP @outerloop
  974.   @fertig:
  975.  
  976.  END; {of ASM}
  977.  MOVE(neue_Tabelle,@ShadowTab^,256); {activate new color table}
  978.  Schatten:=brightness
  979. END;
  980.  
  981. PROCEDURE SetPalette(pal:Palette; update:BOOLEAN);
  982. { in: pal = pointer to palette to be set   }
  983. {     update = TRUE/FALSE for: recompute/don't recompute ShadowTab}
  984. {out: ActualColors = actual color palette  }
  985. {rem: palette has been set and evtl., ShadowTab has been recomputed }
  986. BEGIN
  987.  IF @pal<>@ActualColors
  988.   THEN ActualColors:=pal;  {copy palette into ActualColors         }
  989.  ASM
  990.    MOV SI,OFFSET ActualColors  {DS:SI=^ActualColors[]}
  991.  
  992.    CLI
  993.  
  994.     mov dx,StatusReg
  995.   @WaitNotVSyncLoop:
  996.     in   al,dx
  997.     and  al,8
  998.     jnz  @WaitNotVSyncLoop
  999.   @WaitVSyncLoop:
  1000.     in   al,dx
  1001.     and  al,8
  1002.     jz   @WaitVSyncLoop
  1003.  
  1004.    MOV DX,3C8h
  1005.    XOR AL,AL
  1006.    OUT DX,AL
  1007.    INC DX
  1008.  
  1009.    MOV CX,256
  1010.   @L1:
  1011.    LODSB
  1012.    OUT DX,AL
  1013.    LODSB
  1014.    OUT DX,AL
  1015.    LODSB
  1016.    OUT DX,AL
  1017.    LOOP @L1
  1018.  
  1019.    STI
  1020.  END; {of ASM}
  1021.  IF update THEN SetShadowTab(Schatten)
  1022. END;
  1023.  
  1024. PROCEDURE GetPalette(VAR pal:Palette); ASSEMBLER;
  1025. { in: pal = pointer to palette memory  }
  1026. {out: pal = actually set palette       }
  1027. ASM
  1028.    CLI
  1029.    XOR AL,AL
  1030.    MOV DX,3C7h
  1031.    OUT DX,AL
  1032.    LES DI,pal
  1033.    MOV CX,768
  1034.    MOV DX,3C9h
  1035.   @L1:
  1036.    IN AL,DX
  1037.    STOSB
  1038.    LOOP @L1
  1039.    STI
  1040. END;
  1041.  
  1042. FUNCTION LoadPalette(name:String; number:BYTE; VAR pal:Palette):WORD;
  1043. { in: name   = name of the palette file (type: "*.PAL") to load  }
  1044. {     number = number for the first color being read in from the file     }
  1045. {     ActualColors = actually set color palette }
  1046. {out: number of colors read from the file (0 = an error occured)          }
  1047. {     pal = color palette read from the file, evtl. filled up}
  1048. {rem: All entries in "pal" which get not overwritten by the file's contents}
  1049. {     will become set to the actually set colors of "ActualColors"; the   }
  1050. {     palette will only become loaded, not actually set!}
  1051. LABEL quitloop;
  1052. VAR len:LONGINT;
  1053.     f:File;
  1054.     i,count:WORD;
  1055.     TempPal:Palette;
  1056.     flag:BOOLEAN;
  1057. BEGIN
  1058.  count:=0;  {number of palette entries read in til now     }
  1059.  assign(f,name);
  1060.  {$I-} reset(f,1); {$I+}
  1061.  if (ioresult<>0)
  1062.   THEN BEGIN  {File doesn't exist or at least not with that path }
  1063.         Error:=Err_FileIO;
  1064.         LoadPalette:=0; exit
  1065.        END;
  1066.  len:=filesize(f);  {determine file length}
  1067.  if (len mod 3<>0) OR (len>3*256) OR (len<3)
  1068.   THEN BEGIN
  1069.         Error:=Err_NoPalette;
  1070.         goto quitloop;
  1071.        END;
  1072.  IF len+number*3>3*256
  1073.   THEN BEGIN
  1074.         Error:=Err_PaletteWontFit;
  1075.         goto quitloop;
  1076.        END;
  1077.  
  1078.  TempPal:=ActualColors; {preset temporary palette with actual colors        }
  1079.  {$I-}
  1080.   blockread(f,TempPal[number],len);
  1081.  {$I+}
  1082.  
  1083.   IF (ioresult<>0)
  1084.    THEN BEGIN
  1085.          Error:=Err_FileIO;
  1086.          goto quitloop;
  1087.         END;
  1088.  
  1089.   flag:=FALSE;
  1090.   FOR i:=number TO Pred(number+(len DIV 3))
  1091.    DO flag:=flag OR (TempPal[i].red>63)
  1092.                  OR (TempPal[i].green>63)
  1093.                  OR (TempPal[i].blue>63);
  1094.   IF flag
  1095.    THEN BEGIN
  1096.          Error:=Err_NoPalette;
  1097.          goto quitloop;
  1098.         END;
  1099.  
  1100.   {everything went alright: return palette}
  1101.   pal:=TempPal;
  1102.   count:=len DIV 3;
  1103.  
  1104. quitloop: ;
  1105.  close(f);
  1106.  LoadPalette:=count
  1107. END;
  1108.  
  1109. {Now, all the code pieces follow, which can be used to display a sprite; }
  1110. {the interface used is the same for all of them:                         }
  1111. { in: CX    = number of bytes, which have to be copied from...                                   }
  1112. {     DS:SI = (pointer to source address) to...                          }
  1113. {     ES:BX = (pointer to destination address)                           }
  1114. {     DI    = bitplane (0..3) (=X-coordinate AND 3)                      }
  1115. {     The proper bitmask for selecting the correct write plane has al-   }
  1116. {     ready been set, but not that for the evtl. needed read plane!               }
  1117. {     The routines can rely upon CX being <>0                            }
  1118. {rem: Every routine MUST CONSIST OF EXACTLY 16 bytes and BE FULLY RELO-  }
  1119. {     CATIBLE and MAY NOT CHANGE registers BP,DS and ES!!!!!!!!!!!!!!!!! }
  1120. {     Besides that, for distinguishtability, the routines must be pair-  }
  1121. {     wise disjoint in their first two bytes!                            }
  1122.  
  1123. PROCEDURE Modus0; ASSEMBLER;
  1124. {mode 0 considers color 0 being transparent for background data      }
  1125. ASM
  1126.    INC CX
  1127.    STC                {change BX such that (together with SI) it can be  }
  1128.    SBB BX,SI          {used for accessing the target address}
  1129.  @L1:
  1130.    LODSB              {fetch sprite byte }
  1131.    OR AL,AL           {is it zero?      }
  1132.    LOOPZ @L1          {yes, ignore it   }
  1133.    JCXZ @L2           {all bytes done?  }
  1134.    MOV ES:[BX+SI],AL  {no, store to screen}
  1135.    JMP @L1 {short}    {work on next byte   }
  1136.  @L2:
  1137. END;
  1138.  
  1139. PROCEDURE Modus1; ASSEMBLER;
  1140. {mode 1 writes the data directly to the screen, without further processing }
  1141. ASM
  1142.    MOV DI,BX          {set DI so that the string instructions can be used     }
  1143.    XOR AX,AX          {set AX:=0   }
  1144.    SHR CX,1           {number of words to move         }
  1145.    REP MOVSW          {move data as one block at once  }
  1146.    ADC CX,AX          {does a single byte remain?      }
  1147.    REP MOVSB
  1148.    MOV AX,AX          {4 filling bytes; faster than 4 NOPs}
  1149.    MOV AX,AX
  1150. END;
  1151.  
  1152. PROCEDURE Modus2Work; ASSEMBLER;
  1153. {Continuation of mode2 - everything, which didn't fit in the reserved 16    }
  1154. {bytes is placed here}
  1155. ASM
  1156.    OUT DX,AX          {enable read access for correct plane       }
  1157.  
  1158.    PUSH DS            {DS still points to sprite data, but must    }
  1159.                       {point to background!                         }
  1160.    MOV AX,ES          {DS:SI := ES:DI  (source ptr:=dest. ptr)     }
  1161.    MOV DS,AX
  1162.    MOV SI,DI
  1163.    MOV BX,OFFSET ShadowTab   {set pointer to color lookup table    }
  1164.  
  1165.  @L4:
  1166.    LODSB              {get background color...    }
  1167.    SEGCS XLAT         {...use color lookup table to transform}
  1168.    STOSB              {...and display on actual graphic page}
  1169.    LOOP @L4
  1170.  
  1171.    POP DS
  1172. END;
  1173.  
  1174. PROCEDURE Modus2; ASSEMBLER;
  1175. {mode 2 is thought for "shadows" and the like: the sprite's data itself }
  1176. {will be ignored; instead, the background data underneath the sprite's  }
  1177. {position is read in and these color values will be exchanged against   }
  1178. {those of the color lookup table "ShadowTab" (e.g.: to realize shadows, }
  1179. {this table should hold a darker color for each of the original colors)  }
  1180. ASM
  1181.    MOV AX,DI          {bring bitplane for read access to AX     }
  1182.    MOV DI,BX          {put dest. addr. into DI for 8086's string instructions}
  1183.    MOV AH,AL          {bring bitplane to highbyte   }
  1184.    MOV AL,4
  1185.    MOV DX,3CEh
  1186.    MOV SI,OFFSET Modus2Work  {sort of hack: "CALL Modus2Work" would be coded}
  1187.    CALL SI                   {RELATIVE - and thus, jump to ever-neverland!  }
  1188. END;
  1189.  
  1190. PROCEDURE Modus3Work; ASSEMBLER;
  1191. {continuation of Modus3 - everything, which didn't fit into 16 bytes        }
  1192. {bytes is placed here}
  1193. ASM
  1194.    STC           
  1195.    SBB BX,SI      {address source and destination with only 1 index register}
  1196.    MOV DX,BP      {save BP-register  }
  1197.    MOV BP,BX
  1198.    MOV BX,OFFSET ShadowTab   {set pointer to color lookup table   }
  1199.  
  1200.  @L1:
  1201.    LODSB              {get sprite data...    }
  1202.    OR AL,AL           {  (ignore color 0 as "transparent")      }
  1203.    LOOPZ @L1
  1204.    JCXZ @L2
  1205.    MOV AL,ES:[BP+SI]  {get background color...    }
  1206.    SEGCS XLAT         {...use color lookup table to transform}
  1207.    MOV ES:[BP+SI],AL  {...and display on actual graphic page}
  1208.    JMP @L1
  1209.  @L2:
  1210.    MOV BP,DX          {restore old contents of BP register }
  1211. END;
  1212.  
  1213. PROCEDURE Modus3; ASSEMBLER;
  1214. {Modus3 is thought for "shadows", too: in this mode, all sprite             }
  1215. {pixels with color <>0 will be processed: the background color, which is    }
  1216. {underneath these pixels will be replaced by the corresponding color entry  }
  1217. {from the table "ShadowTab"                                                 }
  1218. {In other words: this mode is equivalent to Modus2, with the exception      }
  1219. {that sprite color 0 is treated as being transparent for shadows            }
  1220. {too!      }
  1221. ASM
  1222.    MOV DX,3CEh        {prepare access to read plane:     }
  1223.    MOV AX,DI          
  1224.    MOV AH,AL          {load read plane into AH  }
  1225.    MOV AL,4
  1226.    OUT DX,AX          {enable read access for correct plane       }
  1227.    INC CX             {inc. number of bytes by 1 (-> LODSB!)  }
  1228.    MOV AX,OFFSET Modus3Work  {trick to make code relocatible!   }
  1229.    CALL AX
  1230. END;
  1231.  
  1232. PROCEDURE Adressen; ASSEMBLER;
  1233. {table with the starting addresses of the 3 routines in the code segment}
  1234. ASM
  1235.    DW OFFSET Modus0
  1236.    DW OFFSET Modus1
  1237.    DW OFFSET Modus2
  1238.    DW OFFSET Modus3
  1239. END;
  1240.  
  1241.  
  1242. PROCEDURE GADR; ASSEMBLER;
  1243. {table with graphic rows starting addresses (offset part)}
  1244. ASM
  1245.    DW $0000,$0050,$00A0,$00F0,$0140,$0190,$01E0,$0230
  1246.    DW $0280,$02D0,$0320,$0370,$03C0,$0410,$0460,$04B0
  1247.    DW $0500,$0550,$05A0,$05F0,$0640,$0690,$06E0,$0730
  1248.    DW $0780,$07D0,$0820,$0870,$08C0,$0910,$0960,$09B0
  1249.    DW $0A00,$0A50,$0AA0,$0AF0,$0B40,$0B90,$0BE0,$0C30
  1250.    DW $0C80,$0CD0,$0D20,$0D70,$0DC0,$0E10,$0E60,$0EB0
  1251.    DW $0F00,$0F50,$0FA0,$0FF0,$1040,$1090,$10E0,$1130
  1252.    DW $1180,$11D0,$1220,$1270,$12C0,$1310,$1360,$13B0
  1253.    DW $1400,$1450,$14A0,$14F0,$1540,$1590,$15E0,$1630
  1254.    DW $1680,$16D0,$1720,$1770,$17C0,$1810,$1860,$18B0
  1255.    DW $1900,$1950,$19A0,$19F0,$1A40,$1A90,$1AE0,$1B30
  1256.    DW $1B80,$1BD0,$1C20,$1C70,$1CC0,$1D10,$1D60,$1DB0
  1257.    DW $1E00,$1E50,$1EA0,$1EF0,$1F40,$1F90,$1FE0,$2030
  1258.    DW $2080,$20D0,$2120,$2170,$21C0,$2210,$2260,$22B0
  1259.    DW $2300,$2350,$23A0,$23F0,$2440,$2490,$24E0,$2530
  1260.    DW $2580,$25D0,$2620,$2670,$26C0,$2710,$2760,$27B0
  1261.    DW $2800,$2850,$28A0,$28F0,$2940,$2990,$29E0,$2A30
  1262.    DW $2A80,$2AD0,$2B20,$2B70,$2BC0,$2C10,$2C60,$2CB0
  1263.    DW $2D00,$2D50,$2DA0,$2DF0,$2E40,$2E90,$2EE0,$2F30
  1264.    DW $2F80,$2FD0,$3020,$3070,$30C0,$3110,$3160,$31B0
  1265.    DW $3200,$3250,$32A0,$32F0,$3340,$3390,$33E0,$3430
  1266.    DW $3480,$34D0,$3520,$3570,$35C0,$3610,$3660,$36B0
  1267.    DW $3700,$3750,$37A0,$37F0,$3840,$3890,$38E0,$3930
  1268.    DW $3980,$39D0,$3A20,$3A70,$3AC0,$3B10,$3B60,$3BB0
  1269.    DW $3C00,$3C50,$3CA0,$3CF0,$3D40,$3D90,$3DE0,$3E30
  1270. END;
  1271.  
  1272.  
  1273. FUNCTION AT:BOOLEAN;
  1274. { in: - }
  1275. {out: TRUE/FALSE, if the machine is (at least) an AT       }
  1276. BEGIN
  1277.  AT:=MEM[$F000:$FFFE]=$FC
  1278. END;
  1279.  
  1280.  
  1281. PROCEDURE SetCycleTime(milliseconds:WORD);
  1282. { in: min. time for one animation cycle in milliseconds  }
  1283. {out: CycleTime := that value in microseconds  }
  1284. {     TimeFlag  := $80}
  1285. {rem: Because of TimeFlag:=$80, the timing mechanism won't work  }
  1286. {     for the very first animation cycle, yet!                   }
  1287. {     If you don't use the timing mechanism (by supplying a value}
  1288. {     of 0 milliseconds), this will result in IsAT:=$80, that is }
  1289. {     the routine will fake "computer is a PC". Else IsAT=0      }
  1290. BEGIN
  1291.  TimeFlag:=$80;
  1292.  CycleTime:=LONGINT(milliseconds)*LONGINT(1000);
  1293.  IF (milliseconds<>0) AND AT
  1294.   THEN IsAT:=0     {yes, time control mechanism shall be used  }
  1295.   ELSE IsAT:=$80   {no, none possible or not wanted           }
  1296. END;
  1297.  
  1298. PROCEDURE SetSpriteCycle(nr,len:WORD);
  1299. { in: nr  = spriteloadnumber of the first sprite in the cycle      }
  1300. {     len = length of your sprite cycle                         }
  1301. {out: NextSprite[nr] through NextSprite[nr+len-1] have been set }
  1302. {     set such that they build a "ring", that is: together      }
  1303. {     they make up a sprite cycle}
  1304. {rem: If the sprite cycle shall consist of (physical) sprites     }
  1305. {     whose load numbers aren't consecutive, then you           }
  1306. {     have to make the appropriate entries into NextSprite[]    }
  1307. {     yourself manually }
  1308. {     This routine uses spriteLOADnumbers!      }
  1309. VAR i:WORD;
  1310. BEGIN
  1311.  IF (nr<1) OR (nr+len-1>LoadMAX)
  1312.   THEN Error:=Err_InvalidSpriteLoadNumber
  1313.   ELSE BEGIN
  1314.         FOR i:=nr TO nr+len-2 DO NextSprite[i]:=SUCC(i);
  1315.         NextSprite[PRED(nr+len)]:=nr  {last sprite points to first one}
  1316.        END;
  1317. END;
  1318.  
  1319.  
  1320. FUNCTION GetImage(x1,y1,x2,y2:INTEGER; pa:BYTE):POINTER;
  1321. { in: (x1,y1) = upper left corner of the area which shall be stored        }
  1322. {     (x2,y2) = according lower right corner (in virtual coordinates!)     }
  1323. {     pa      = graphic page from which the image should be taken (0..2)   }
  1324. {     StartVirtualX,StartVirtualY = upper left image corner                }
  1325. {out: pointer to heap address where the copied screen area is stored       }
  1326. {     left_cut= evtl. needed left cut off of the image (this determines,   }
  1327. {               how many pixels at the left edge of the image lie outside  }
  1328. {               of the screen)                                              }
  1329. {     right_cut,top_cut,bottom_cut = dto., for the other boundaries             }
  1330. {     was_cut = TRUE/FALSE, if it was necessary/not necessary to clip the  }
  1331. {               fetched image                                              }
  1332. {rem: The memory needed will be reserved by the routine automatically      }
  1333. {     If that is impossible (or the image is completely offscreen), the    }
  1334. {     routine will return NIL!                                             }
  1335. {     Only if "was_cut" is set to TRUE, the (global) "..._cut" variables   }
  1336. {     will be set to something other then 0, that is: if the window lies   }
  1337. {     _completely_ offscreen (this means: returned ptr=NIL), then the      }
  1338. {     routine still returns "was_cut"=FALSE!}
  1339. VAR len,breite,hoehe,StartAdr,actualAdr,SegmAdr:WORD;
  1340.     p:POINTER;
  1341. BEGIN
  1342.  was_cut:=FALSE; left_cut:=0; right_cut:=0; top_cut:=0; bottom_cut:=0;
  1343.  dec(x1,StartVirtualX);   {compute screen coordinates     }
  1344.  dec(y1,StartVirtualY);
  1345.  IF (x1>XMAX) or (y1>YMAX) or (x2<0) or (y2<0) or (x1>x2) or (y1>y2)
  1346.   THEN BEGIN  {window is offscreen                    }
  1347.         GetImage:=NIL;
  1348.         exit
  1349.        END;
  1350.  {cut clipping according to visible screen:}
  1351.  IF x1<0 THEN BEGIN left_cut :=-x1; x1:=0; was_cut:=TRUE END;
  1352.  IF y1<0 THEN BEGIN top_cut:=-y1; y1:=0; was_cut:=TRUE END;
  1353.  IF x2>XMAX THEN BEGIN right_cut :=x2-XMAX; x2:=XMAX; was_cut:=TRUE END;
  1354.  IF y2>YMAX THEN BEGIN bottom_cut:=y2-YMAX; y2:=YMAX; was_cut:=TRUE END;
  1355.  
  1356.  breite:=SUCC(x2-x1); hoehe:=SUCC(y2-y1);
  1357.  len:=breite*hoehe+2*2; {1 pixel=1 byte; add 2 words for width & height     }
  1358.  IF len>MaxAvail
  1359.   THEN BEGIN
  1360.         Error:=Err_NotEnoughMemory;
  1361.         GetImage:=NIL;
  1362.         exit
  1363.        END;
  1364.  IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE)  {page number must be 0..2   }
  1365.   THEN BEGIN
  1366.         Error:=Err_InvalidPageNumber;
  1367.         GetImage:=NIL;
  1368.         exit
  1369.        END
  1370.   ELSE SegmAdr:=Segment_Adr[pa];
  1371.  GetMem(p,len);         {get memory from the heap      }
  1372.  ASM
  1373.     CLD
  1374.     LES DI,p        {ES:DI = pointer to the acquired memory   }
  1375.     MOV AX,breite
  1376.     STOSW           {store width first...    }
  1377.     MOV AX,hoehe
  1378.     STOSW           {...then store height, followed by the data     }
  1379.  
  1380.     MOV BX,AX       {BX:=height (to be used later) }
  1381.     MOV SI,y1
  1382.     SHL SI,1
  1383.     MOV SI,CS:[OFFSET gadr + SI]   {SI:=y1*LINESIZE}
  1384.     MOV AX,x1
  1385.     MOV DL,AL
  1386.     SHR AX,1
  1387.     SHR AX,1
  1388.     ADD SI,AX       {SI:=offset part of the start address}
  1389.     MOV StartAdr,SI
  1390.     MOV actualAdr,SI
  1391.     AND DL,3
  1392.     MOV AH,DL
  1393.     MOV AL,4
  1394.     MOV DX,3CEh
  1395.     OUT DX,AX       {select start plane  }
  1396.     MOV DS,SegmAdr
  1397.  
  1398.     {DS:SI = pointer to first byte to store; ES:DI = its target address     }
  1399.     {AH = startplane, AL = 4, BX = number of rows to process  }
  1400.  
  1401.     MOV DX,breite
  1402.     ADD DX,3
  1403.     SHR DX,1
  1404.     SHR DX,1        {DX = number of bytes to store for each row}
  1405.  
  1406.   @L1:
  1407.     MOV CX,DX       {store data of one row        }
  1408.     SHR CX,1        {faster than "REP MOVSB"  }
  1409.     REP MOVSW
  1410.     ADC CX,0
  1411.     REP MOVSB
  1412.     MOV SI,actualAdr  {increment source pointer by 1 graphic row}
  1413.     ADD SI,LINESIZE
  1414.     MOV actualAdr,SI
  1415.     DEC BX          {decrease row counter    }
  1416.     JNE @L1
  1417.  
  1418.     INC AH          {select next plane       }
  1419.     CMP AH,4
  1420.     JNE @nowrap1    {wrapping the bitplane means: start address  }
  1421.     MOV AH,0        {needs mending: increment address by 1! }
  1422.     INC StartAdr
  1423.   @nowrap1:
  1424.     MOV DX,3CEh
  1425.     OUT DX,AX
  1426.     MOV BX,hoehe
  1427.     MOV DX,breite
  1428.     INC DX
  1429.     INC DX
  1430.     SHR DX,1
  1431.     SHR DX,1
  1432.     MOV SI,StartAdr
  1433.     MOV actualAdr,SI
  1434.  
  1435.   @L2:
  1436.     MOV CX,DX
  1437.     SHR CX,1        {faster than "REP MOVSB"  }
  1438.     REP MOVSW
  1439.     ADC CX,0
  1440.     REP MOVSB
  1441.     MOV SI,actualAdr
  1442.     ADD SI,LINESIZE
  1443.     MOV actualAdr,SI
  1444.     DEC BX
  1445.     JNE @L2
  1446.  
  1447.     INC AH
  1448.     CMP AH,4
  1449.     JNE @nowrap2
  1450.     MOV AH,0
  1451.     INC StartAdr
  1452.   @nowrap2:
  1453.     MOV DX,3CEh
  1454.     OUT DX,AX
  1455.     MOV BX,hoehe
  1456.     MOV DX,breite
  1457.     INC DX
  1458.     SHR DX,1
  1459.     SHR DX,1
  1460.     MOV SI,StartAdr
  1461.     MOV actualAdr,SI
  1462.  
  1463.   @L3:
  1464.     MOV CX,DX
  1465.     SHR CX,1        {faster than "REP MOVSB"  }
  1466.     REP MOVSW
  1467.     ADC CX,0
  1468.     REP MOVSB
  1469.     MOV SI,actualAdr
  1470.     ADD SI,LINESIZE
  1471.     MOV actualAdr,SI
  1472.     DEC BX
  1473.     JNE @L3
  1474.  
  1475.     INC AH
  1476.     CMP AH,4
  1477.     JNE @nowrap3
  1478.     MOV AH,0
  1479.     INC StartAdr
  1480.   @nowrap3:
  1481.     MOV DX,3CEh
  1482.     OUT DX,AX
  1483.     MOV BX,hoehe
  1484.     MOV DX,breite
  1485.     SHR DX,1
  1486.     SHR DX,1
  1487.     MOV SI,StartAdr
  1488.     MOV actualAdr,SI
  1489.  
  1490.   @L4:
  1491.     MOV CX,DX
  1492.     SHR CX,1        {faster than "REP MOVSB"  }
  1493.     REP MOVSW
  1494.     ADC CX,0
  1495.     REP MOVSB
  1496.     MOV SI,actualAdr
  1497.     ADD SI,LINESIZE
  1498.     MOV actualAdr,SI
  1499.     DEC BX
  1500.     JNE @L4
  1501.  
  1502.     MOV AX,SEG @DATA
  1503.     MOV DS,AX
  1504.  END;
  1505.  GetImage:=p
  1506. END;
  1507.  
  1508. PROCEDURE PutImage(x,y:INTEGER; p:POINTER; pa:BYTE);
  1509. { in: (x,y) = upper left corner of destination (virtual coordinates)    }
  1510. {     p     = pointer to the cutting (returned by GetImage)             }
  1511. {     pa    = graphic page to which the cutting shall be pasted to      }
  1512. {     StartVirtualX,StartVirtualY = upper left image corner             }
  1513. {out: - }
  1514. {rem: The cutting has been properly clipped before being displayed      }
  1515. {     If you supply NIL as pointer, the routine will display nothing    }
  1516. {     That is useful when you are going to use the routine directly on   }
  1517. {     the results supplied by GetImage!                                                     }
  1518. VAR breite,hoehe,SegmAdr,actualAdr,StartAdr,breite1,breite2,breite3,breite4,
  1519.     licut_div4,topcut,pl_adr1,pl_adr2,pl_adr3,pl_adr4:WORD;
  1520.     licutoff,temp:INTEGER;
  1521. BEGIN
  1522.  IF p=NIL THEN exit;
  1523.  dec(x,StartVirtualX);   {compute screen coordinates     }
  1524.  dec(y,StartVirtualY);
  1525.  IF (x>XMAX) or (y>YMAX) THEN exit;
  1526.  IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE)
  1527.   THEN BEGIN
  1528.         Error:=Err_InvalidPageNumber;
  1529.         exit
  1530.        END
  1531.   ELSE SegmAdr:=Segment_Adr[pa];
  1532.  breite:=MEMW[SEG(p^):OFS(p^)];
  1533.  hoehe :=MEMW[SEG(p^):OFS(p^)+2];
  1534.  IF (x+breite<=0) or (y+hoehe<=0) THEN exit;
  1535.  IF x<0 THEN BEGIN licutoff:=-x; x:=0 END
  1536.         ELSE licutoff:=0;
  1537.  IF y<0 THEN BEGIN
  1538.               topcut:=-y;
  1539.               y:=0
  1540.              END
  1541.         ELSE topcut:=0;
  1542.  
  1543.  breite1:=(breite + 3) shr 2;  {Width of a row for the first, second,     }
  1544.  breite2:=(breite + 2) shr 2;  {third and fourth bitplane, respectively}
  1545.  breite3:=(breite + 1) shr 2;
  1546.  breite4:=(breite + 0) shr 2;
  1547.  
  1548.  {Compute starting addresses of the 4 bitplanes; take into account evtl.  }
  1549.  {left cutoff (+4 bytes to jump over "breite" (width) and "hoehe" (height)    }
  1550.  licut_div4:=licutoff shr 2;
  1551.  pl_adr1:=4 +licut_div4 +topcut*breite1;
  1552.  pl_adr2:=4 +licut_div4 +topcut*breite2 +hoehe*breite1;
  1553.  pl_adr3:=4 +licut_div4 +topcut*breite3 +hoehe*(breite1+breite2);
  1554.  pl_adr4:=4 +licut_div4 +topcut*breite4 +hoehe*(breite1+breite2+breite3);
  1555.  
  1556.  {licutoff mod 4 determines the order in which the points must be read   }
  1557.  {of the heap: 0 = plane order (1,2,3,4); 1 = plane order (2,3,4,1);     }
  1558.  {2=(3,4,1,2); 3=(4,1,2,3); note that the widths of the bitplane tables  }
  1559.  {are (and remain) linked to these; therefore, they will be swapped too  }
  1560.  {to accomplish that!      }
  1561.  ASM
  1562.     CLD
  1563.     MOV AX,licutoff
  1564.     AND AL,3
  1565.     OR AL,AL
  1566.     JE @no_exchange
  1567.     CMP AL,1
  1568.     JNE @L10
  1569.  
  1570.     MOV AX,pl_adr2     {displacement of 1 bit: }
  1571.     MOV BX,pl_adr3     {AX=Plane2,BX=Plane3,CX=Plane4,DX=Plane1}
  1572.     MOV CX,pl_adr4
  1573.     MOV DX,pl_adr1     {wrap-around, thus: increment address by 1, which  }
  1574.     INC DX             {corresponds to an ajustment of 4 points           }
  1575.     MOV pl_adr1,AX     {(e.g.: pixels (1,5,9,...),(2,6,10,...),(3,7,11,...)}
  1576.     MOV pl_adr2,BX     {and (0,4,8,...); the last bitplane needs a cor-   }
  1577.     MOV pl_adr3,CX     {rection of +1 byte: this results in (4,8,12,...)    }
  1578.     MOV pl_adr4,DX     {(read planes top-down, in alternating order!)     }
  1579.     MOV AX,breite2     {Now the plane widths: }
  1580.     MOV BX,breite3     {AX=Plane2,BX=Plane3,CX=Plane4,DX=Plane1}
  1581.     MOV CX,breite4
  1582.     MOV DX,breite1
  1583.     JMP @store
  1584.  
  1585.   @L10:
  1586.     CMP AL,2
  1587.     JNE @L20
  1588.  
  1589.     MOV AX,pl_adr3     {displacement of 2 bit: }
  1590.     MOV BX,pl_adr4     {AX=Plane3,BX=Plane4,CX=Plane1,DX=Plane2}
  1591.     MOV CX,pl_adr1
  1592.     INC CX
  1593.     MOV DX,pl_adr2
  1594.     INC DX
  1595.     MOV pl_adr1,AX
  1596.     MOV pl_adr2,BX
  1597.     MOV pl_adr3,CX
  1598.     MOV pl_adr4,DX
  1599.     MOV AX,breite3     {dto. for plane widths:  }
  1600.     MOV BX,breite4     {AX=Plane3,BX=Plane4,CX=Plane1,DX=Plane2}
  1601.     MOV CX,breite1
  1602.     MOV DX,breite2
  1603.     JMP @store
  1604.   @L20:
  1605.     MOV AX,pl_adr4     {displacement of 3 bit: }
  1606.     MOV BX,pl_adr1     {AX=Plane4,BX=Plane1,CX=Plane2,DX=Plane3}
  1607.     INC BX
  1608.     MOV CX,pl_adr2
  1609.     INC CX
  1610.     MOV DX,pl_adr3
  1611.     INC DX
  1612.     MOV pl_adr1,AX
  1613.     MOV pl_adr2,BX
  1614.     MOV pl_adr3,CX
  1615.     MOV pl_adr4,DX
  1616.     MOV AX,breite4     {dto. for plane widths:  }
  1617.     MOV BX,breite1     {AX=Plane4,BX=Plane1,CX=Plane2,DX=Plane3}
  1618.     MOV CX,breite2
  1619.     MOV DX,breite3
  1620.   @store:
  1621.     MOV breite1,AX
  1622.     MOV breite2,BX
  1623.     MOV breite3,CX
  1624.     MOV breite4,DX
  1625.  
  1626.   @no_exchange:        {precondition here: (pl_adr?,breite?) contain the   }
  1627.                        {source bitplanes/-widths in the correct order      }
  1628.  
  1629.     MOV AX,topcut
  1630.     SUB hoehe,AX       {evtl. adjust height for upper cutoff    }
  1631.     MOV AX,licutoff
  1632.     SUB breite,AX      {dto. for width and left cutoff    }
  1633.  
  1634.     MOV AX,x           {if image would spread over the right screen  }
  1635.     ADD AX,breite      {boundary: compute right cutoff               }
  1636.     SUB AX,XMAX+1
  1637.     JLE @no_recutoff
  1638.     SUB breite,AX      {cut off AX points at the right}
  1639.   @no_recutoff:
  1640.  
  1641.     MOV AX,y           {exactly the same for the lower screen border}
  1642.     ADD AX,hoehe
  1643.     SUB AX,YMAX+1
  1644.     JLE @no_bocutoff
  1645.     SUB hoehe,AX       {cut off AX rows at the bottom}
  1646.   @no_bocutoff:
  1647.  
  1648.  
  1649.     LDS SI,p
  1650.     ADD pl_adr2,SI     {add pointer's offset part to the plane address    }
  1651.     ADD pl_adr3,SI
  1652.     ADD pl_adr4,SI
  1653.  
  1654.     ADD SI,pl_adr1     {width,height and parts above the screen        }
  1655.     MOV ES,SegmAdr
  1656.  
  1657.     MOV DI,y
  1658.     SHL DI,1
  1659.     MOV DI,CS:[OFFSET gadr + DI]  {DI:=y*LINESIZE}
  1660.     MOV AX,x
  1661.     MOV BL,AL
  1662.     SHR AX,1
  1663.     SHR AX,1
  1664.     ADD DI,AX         {DI:=y*LINESIZE +(x DIV 4)}
  1665.     MOV StartAdr,DI
  1666.     MOV actualAdr,DI
  1667.  
  1668.     AND BX,3          {startplane:=x mod 3}
  1669.     MOV AH,CS:[OFFSET CS_TranslateTab + BX]
  1670.     MOV AL,2
  1671.     MOV DX,3C4h
  1672.     OUT DX,AX         {use it as write plane     }
  1673.  
  1674.     MOV DX,hoehe
  1675.     MOV DI,actualAdr
  1676.  
  1677.     {DS:SI = pointer to data, ES:DI = dest. address on screen for them   }
  1678.     {AH = bitmask for access, AL = 2    }
  1679.     MOV BX,breite
  1680.     ADD BX,3
  1681.     SHR BX,1
  1682.     SHR BX,1
  1683.     mov cx,bx
  1684.   @L1:
  1685.     push si
  1686.     SHR CX,1        {faster than "REP MOVSB"  }
  1687.     REP MOVSW
  1688.     ADC CX,0
  1689.     REP MOVSB
  1690.     pop si
  1691.     mov cx,bx
  1692.     add si,breite1
  1693.     MOV DI,actualAdr
  1694.     ADD DI,LINESIZE
  1695.     MOV actualAdr,DI
  1696.     DEC DX
  1697.     JNE @L1
  1698.  
  1699.  
  1700.     SHL AH,1          {select next bitplane; if wrap-around occurs    }
  1701.     CMP AH,16         {from bitplane 3 to bitplane 0, then the start- }
  1702.     JNE @nowrap1      {ing address must be incremented by 1 byte         }
  1703.     MOV AH,1
  1704.     INC StartAdr
  1705.   @nowrap1:
  1706.     MOV DX,3C4h
  1707.     OUT DX,AX
  1708.     MOV SI,pl_adr2
  1709.     MOV DI,StartAdr
  1710.     MOV actualAdr,DI
  1711.     MOV DX,hoehe
  1712.     MOV BX,breite
  1713.     INC BX
  1714.     INC BX
  1715.     SHR BX,1
  1716.     SHR BX,1
  1717.     mov cx,bx
  1718.   @L2:
  1719.     push si
  1720.     SHR CX,1        {faster than "REP MOVSB"  }
  1721.     REP MOVSW
  1722.     ADC CX,0
  1723.     REP MOVSB
  1724.     pop si
  1725.     mov cx,bx
  1726.     add si,breite2
  1727.     MOV DI,actualAdr
  1728.     ADD DI,LINESIZE
  1729.     MOV actualAdr,DI
  1730.     DEC DX
  1731.     JNE @L2
  1732.  
  1733.  
  1734.     SHL AH,1
  1735.     CMP AH,16
  1736.     JNE @nowrap2
  1737.     MOV AH,1
  1738.     INC StartAdr
  1739.   @nowrap2:
  1740.     MOV DX,3C4h
  1741.     OUT DX,AX
  1742.     MOV SI,pl_adr3
  1743.     MOV DI,StartAdr
  1744.     MOV actualAdr,DI
  1745.     MOV DX,hoehe
  1746.     MOV BX,breite
  1747.     INC BX
  1748.     SHR BX,1
  1749.     SHR BX,1
  1750.     mov cx,bx
  1751.   @L3:
  1752.     push si
  1753.     SHR CX,1        {faster than "REP MOVSB"  }
  1754.     REP MOVSW
  1755.     ADC CX,0
  1756.     REP MOVSB
  1757.     pop si
  1758.     mov cx,bx
  1759.     add si,breite3
  1760.     MOV DI,actualAdr
  1761.     ADD DI,LINESIZE
  1762.     MOV actualAdr,DI
  1763.     DEC DX
  1764.     JNE @L3
  1765.  
  1766.  
  1767.     SHL AH,1
  1768.     CMP AH,16
  1769.     JNE @nowrap3
  1770.     MOV AH,1
  1771.     INC StartAdr
  1772.   @nowrap3:
  1773.     MOV DX,3C4h
  1774.     OUT DX,AX
  1775.     MOV SI,pl_adr4
  1776.     MOV DI,StartAdr
  1777.     MOV actualAdr,DI
  1778.     MOV DX,hoehe
  1779.     MOV BX,breite
  1780.     SHR BX,1
  1781.     SHR BX,1
  1782.     mov cx,bx
  1783.   @L4:
  1784.     push si
  1785.     SHR CX,1        {faster than "REP MOVSB"  }
  1786.     REP MOVSW
  1787.     ADC CX,0
  1788.     REP MOVSB
  1789.     pop si
  1790.     mov cx,bx
  1791.     add si,breite4
  1792.     MOV DI,actualAdr
  1793.     ADD DI,LINESIZE
  1794.     MOV actualAdr,DI
  1795.     DEC DX
  1796.     JNE @L4
  1797.  
  1798.     MOV AX,SEG @DATA
  1799.     MOV DS,AX
  1800.  END;
  1801.  
  1802. END;
  1803.  
  1804. PROCEDURE FreeImageMem(p:POINTER);
  1805. { in: p = pointer to image memory, allocated by GetImage()}
  1806. {out: - }
  1807. {rem: the heap memory allocated for the image has been released    }
  1808. BEGIN
  1809.  IF p<>NIL THEN FreeMem(p,MEMW[Seg(p^):Ofs(p^)]*MEMW[Seg(p^):Ofs(p^)+2] + 2*2)
  1810. END;
  1811.  
  1812. PROCEDURE Screen(pa:BYTE);
  1813. { in: pa = graphic page to be shown (0..3)     }
  1814. {out: - }
  1815. {rem: The display has been switched to graphic page pa                 }
  1816. {     The routine does NOT synchronize on any retrace-signal           }
  1817. {     Sensible page values are only 0 or 1 here, but the routine       }
  1818. {     doesn't make any checks!}
  1819. BEGIN
  1820.  ASM
  1821.     MOV DX,CRTAddress        {CRT-Controller}
  1822.     MOV AL,$0D               {LB-startaddress-register}
  1823.     CLI                      {May not be interrupted!              }
  1824.     OUT DX,AL
  1825.     INC DX
  1826.                              {realize "AX:=Offset_Adr[pa]":   }
  1827.     MOV BL,pa
  1828.     MOV SI,BX
  1829.     AND SI,3                 {page value *2 (word-sized entries!)}
  1830.     SHL SI,1                 {add start address of array to that   }
  1831.     ADD SI,OFFSET Offset_Adr-StartIndex*2  {evtl. correct displacement    }
  1832.     LODSW                    {and fetch value}
  1833.     OUT DX,AL                {set LB of new starting address  }
  1834.     DEC DX
  1835.     MOV AL,$0C
  1836.     OUT DX,AL
  1837.     INC DX
  1838.     MOV AL,AH                {set HB of new starting address  }
  1839.     OUT DX,AL
  1840.     STI
  1841.  END;
  1842. END;
  1843.  
  1844. PROCEDURE InitGraph;
  1845. { in: PAGE = actual graphic page }
  1846. {out: - }
  1847. {rem: switches the VGA-card into 320x200x256x4-mode; ATTENTION!      }
  1848. {     This mode is different from mode $13 of the VGA-BIOS!!!          }
  1849. {     The display will be switched to graphic page 1-PAGE            }
  1850. {     The default colors of mode $13 will be set!                    }
  1851. BEGIN
  1852.  ASM
  1853.     MOV AX,0013h   {use BIOS to set graphic mode $13 (320x200x256)   }
  1854.     INT 10h        
  1855.     MOV DX,03C4h   {select memory-mode-register at sequencer port    }
  1856.     MOV AL,04      
  1857.     OUT DX,AL      
  1858.     INC DX         {read in data via the according data register     }
  1859.     IN  AL,DX      
  1860.     AND AL,0F7h    {bit 3:=0 -> don't chain planes }
  1861.     OR  AL,04      {bit 2:=1 -> no odd/even-scheme }
  1862.     OUT DX,AL      {activate new settings          }
  1863.     MOV DX,03C4h   {s.a.: address sequencer reg. 2 (=map-mask),...   }
  1864.     MOV AL,02      
  1865.     OUT DX,AL      
  1866.     INC DX
  1867.     MOV AL,0Fh     {...and allow access to all 4 bit maps            }
  1868.     OUT DX,AL      
  1869.     MOV AX,0A000h  {starting in segment A000h, set 8000h logical     }
  1870.     MOV ES,AX      {words = 4*8000h physical words (because of 4     }
  1871.     SUB DI,DI      {bitplanes) to 0                                  }
  1872.     MOV AX,DI      
  1873.     MOV CX,8000h   
  1874.     CLD
  1875.     REP STOSW
  1876.  
  1877.     MOV DX,CRTAddress  {address the underline-location-register at   }
  1878.     MOV AL,14h     {the CRT-controller port, read out the according  }
  1879.     OUT DX,AL      {data register:                                   }
  1880.     INC DX
  1881.     IN  AL,DX
  1882.     AND AL,0BFh    {bit 6:=0 -> no double word addressing scheme in  }
  1883.     OUT DX,AL      {video ram                                        }
  1884.     DEC DX         
  1885.     MOV AL,17h     {select mode control register                     }
  1886.     OUT DX,AL      
  1887.     INC DX
  1888.     IN  AL,DX
  1889.     OR  AL,40h     {bit 6:=1 -> address memory as a linear bit array     }
  1890.     OUT DX,AL      
  1891.  END;
  1892.  Screen(1-PAGE);  {ALWAYS is the non-actual graphic page the visible one!}
  1893.  SetPalette(DefaultColors,FALSE)  {set default palette, just to be sure!    }
  1894. END;
  1895.  
  1896.  
  1897. PROCEDURE Line(x1,y1,x2,y2:INTEGER; pa:BYTE);
  1898. { in: x1,y1,x2,y2 = coordinates of two points, }
  1899. {     Color       = color (0..255)             }
  1900. {     StartVirtualX,StartVirtualY = upper left image corner            }
  1901. {     pa          = graphic page to be drawn upon (0..2)               }
  1902. {out: - }
  1903. {rem: A line has been drawn between the VIRTUAL points (x1,y1) and (x2,y2)  }
  1904. {     using the color COLOR; the routine will take care of transforming    }
  1905. {     the coordinates to absolute screen coordinates and evtl. necessary   }
  1906. {     clipping actions.                                                    }
  1907. {     The line will NOT automatically be taken over into the background     }
  1908. {     image, that is: it will be visible only for one animation cycle (if     }
  1909. {     it shall stay permanent, you have to draw it into the background!)   }
  1910. {     (For that reason, you should call this routine AFTER calling ANIMATE }
  1911. {     because otherwise, the drawn line will vanish at once!)              }
  1912. CONST CodeLinks =$7;  {%0111}
  1913.       CodeRechts=$B;  {%1011}
  1914.       CodeOben  =$D;  {%1101}
  1915.       CodeUnten =$E;  {%1110}
  1916. BEGIN
  1917.  IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE)
  1918.   THEN Error:=Err_InvalidPageNumber
  1919.   ELSE
  1920.   {first clip line to visible window; use Sutherland-Cohen-algorithm:  }
  1921.   {use 4 bit-codes for: left|right|top|bottom                          }
  1922.   ASM
  1923.      CLD
  1924.      MOV CL,$F         {start with %1111  }
  1925.      MOV AX,x2
  1926.      SUB AX,StartVirtualX   {transform x2 into absolue coordinates}
  1927.      MOV x2,AX
  1928.      OR AX,AX          {x2<0 ?}
  1929.      JL @GC1Punkt2     {yes, don't change flag for "point is left of window"}
  1930.      AND CL,CodeLinks  {no, reset flag        }
  1931.    @GC1Punkt2:
  1932.      CMP AX,XMAX       {x2>XMAX ?}
  1933.      JG @GC2Punkt2     {yes, don't change flag for "point is right of window"}
  1934.      AND CL,CodeRechts {no, reset flag        }
  1935.    @GC2Punkt2:
  1936.      MOV AX,y2
  1937.      SUB AX,StartVirtualY   {transform y2 into absolue coordinates}
  1938.      MOV y2,AX
  1939.      OR AX,AX          {y2<0 ?}
  1940.      JL @GC3Punkt2     {yes, don't change flag for "point is above window"}
  1941.      AND CL,CodeOben   {no, reset flag        }
  1942.    @GC3Punkt2:
  1943.      CMP AX,YMAX       {y2>YMAX ?}
  1944.      JG @GC4Punkt2     {yes, don't change flag for "point is below window"}
  1945.      AND CL,CodeUnten
  1946.    @GC4Punkt2:         {CL holds the area code for point 2            }
  1947.  
  1948.      MOV AX,x1
  1949.      SUB AX,StartVirtualX   {transform x1 into absolue coordinates}
  1950.      MOV x1,AX
  1951.      MOV AX,y1
  1952.      SUB AX,StartVirtualY   {transform y1 into absolue coordinates}
  1953.      MOV y1,AX
  1954.  
  1955.    @Punkt1:
  1956.      MOV CH,$F         {start with %1111  }
  1957.      MOV AX,x1
  1958.      OR AX,AX          {x1<0 ?}
  1959.      JL @GC1Punkt1     {yes, don't change flag for "point is left of window"}
  1960.      AND CH,CodeLinks  {no, reset flag        }
  1961.    @GC1Punkt1:
  1962.      CMP AX,XMAX       {x1>XMAX ?}
  1963.      JG @GC2Punkt1     {yes, don't change flag for "point is right of window"}
  1964.      AND CH,CodeRechts {no, reset flag        }
  1965.    @GC2Punkt1:
  1966.      MOV AX,y1
  1967.      OR AX,AX          {y1<0 ?}
  1968.      JL @GC3Punkt1     {yes, don't change flag for "point is above window"}
  1969.      AND CH,CodeOben   {no, reset flag        }
  1970.    @GC3Punkt1:
  1971.      CMP AX,YMAX       {y1>YMAX ?}
  1972.      JG @GC4Punkt1     {yes, don't change flag for "point is below window"}
  1973.      AND CH,CodeUnten
  1974.    @GC4Punkt1:         {CH holds the area code for point 1            }
  1975.  
  1976.    {CL holds the area code for point 2, CH the one for point 1   }
  1977.  
  1978.      MOV AX,CX
  1979.      AND AL,AH         {Code1 AND Code2 <>0 ?}
  1980.      JNZ @LineReady    {yes, line is completely outside the window}
  1981.      MOV AX,CX
  1982.      OR AL,AH          {Code1 OR Code2 =0 ?}
  1983.      JZ @DrawLine      {yes, line is completely inside the window}
  1984.  
  1985.    {Now do the clipping itself:          }
  1986.      MOV AX,CX
  1987.      OR AH,AH          {Code1 =0 ?}
  1988.      JNZ @CL3          {no, everything ok}
  1989.      MOV AX,x1         {yes, swap points!      }
  1990.      XCHG AX,x2
  1991.      MOV x1,AX
  1992.      MOV AX,y1
  1993.      XCHG AX,y2
  1994.      MOV y1,AX
  1995.      XCHG CL,CH
  1996.    @CL3:
  1997.      MOV AL,CH        {AL:=Code1}
  1998.      MOV BX,x2
  1999.      SUB BX,x1        {BX:=x2-x1}
  2000.      MOV SI,y2
  2001.      SUB SI,y1        {SI:=y2-y1}
  2002.      TEST AL,NOT CodeLinks     {point1 left of window?   }
  2003.      JZ @CL4                   {no  }
  2004.      {yes, compute new coordinates: y1:=y1+(y2-y1)/(x2-x1)*(WindowX1-X1)   }
  2005.      {and x1:=WindowX1   (here, WindowX1 = 0)     }
  2006.      XOR AX,AX
  2007.      XCHG AX,x1       {x1:=0}
  2008.      NEG AX           {AX:=-x1old}
  2009.      IMUL SI
  2010.      IDIV BX
  2011.      ADD y1,AX
  2012.      JMP @Punkt1
  2013.  
  2014.    @CL4:
  2015.      TEST AL,NOT CodeRechts    {point1 right of window?   }
  2016.      JZ @CL5                   {no  }
  2017.      {yes, compute y1:=y1+(y2-y1)/(x2-x1)*(WindowX2-X1), x1:=WindowX2  }
  2018.      { (here, WindowX2=XMAX) }
  2019.      MOV AX,XMAX
  2020.      SUB AX,x1
  2021.      IMUL SI
  2022.      IDIV BX
  2023.      ADD y1,AX
  2024.      MOV x1,XMAX
  2025.      JMP @Punkt1
  2026.  
  2027.    @CL5:
  2028.      TEST AL,NOT CodeOben      {point1 above window?        }
  2029.      JZ @CL6                   {no  }
  2030.      {yes, compute x1:=x1+(x2-x1)/(y2-y1)*(WindowY1-y1), y1:=WindowY1 }
  2031.      { (here, WindowY1=0) }
  2032.      XOR AX,AX
  2033.      XCHG AX,y1
  2034.      NEG AX
  2035.      IMUL BX
  2036.      IDIV SI
  2037.      ADD x1,AX
  2038.      JMP @Punkt1
  2039.  
  2040.    @CL6:
  2041.      TEST AL,NOT CodeUnten     {point below window?         }
  2042.      JZ @Punkt1                {no  }
  2043.      {yes, compute x1:=x1+(x2-x1)/(y2-y1)*(WindowY2-y1), y1:=WindowY2 }
  2044.      { (here, WindowY2=YMAX) }
  2045.      MOV AX,YMAX
  2046.      SUB AX,y1
  2047.      IMUL BX
  2048.      IDIV SI
  2049.      ADD x1,AX
  2050.      MOV y1,YMAX
  2051.      JMP @Punkt1
  2052.  
  2053.    {precondition here: both points have been clipped to the visible window;}
  2054.    {if the line is completely offscreen, the program jumped directly to    }
  2055.    {@LineReady, instead!            }
  2056.    @DrawLine:
  2057.      PUSH BP
  2058.      MOV Steigung,0  {reset Flag        }
  2059.      MOV CX,x2
  2060.      SUB CX,x1       {point1 right of point2 ?  }
  2061.      JGE @posDX      {no  }
  2062.      NEG CX          {yes, swap points      }
  2063.      MOV AX,x1
  2064.      XCHG AX,x2
  2065.      MOV x1,AX
  2066.      MOV AX,y1
  2067.      XCHG AX,y2
  2068.      MOV y1,AX
  2069.  
  2070.    @posDX:
  2071.      MOV DI,y1
  2072.      SHL DI,1
  2073.      MOV DI,CS:[OFFSET gadr + DI]   {DI:=y1*LINESIZE}
  2074.      MOV AX,x1
  2075.      MOV BL,AL
  2076.      SHR AX,1
  2077.      SHR AX,1
  2078.      ADD DI,AX       {DI:=y1*LINESIZE+(x1 DIV 4) }
  2079.  
  2080.      AND BX,3        {BX:=(x1 AND 4) }
  2081.      MOV DH,[OFFSET TranslateTab + BX]  {get mask for VRAM-access     }
  2082.      MOV DL,2
  2083.  
  2084.      MOV BL,pa       {BH=0 -> BX=drawing page}
  2085.      SHL BX,1
  2086.      ADD BX,OFFSET Segment_Adr -StartIndex*2
  2087.      MOV ES,[BX]
  2088.  
  2089.      {ES:DI=pointer to graphic address of point1, DX=access mask for it }
  2090.      MOV SI,LINESIZE
  2091.      MOV BX,y2
  2092.      SUB BX,y1       {point1 below point2 ?        }
  2093.      JG @posDY       {no  }
  2094.      NEG BX          {yes, negate deltaY and row-increment   }
  2095.      NEG SI
  2096.  
  2097.    @posDY:
  2098.      CMP BX,CX       {deltaY>deltaX ?}
  2099.      JLE @flach      {no: small slope, <=1        }
  2100.      XCHG BX,CX      {yes, swap deltas and set flag         }
  2101.      MOV Steigung,1
  2102.  
  2103.    {compute Bresenham-parameters: 2*DY, 2*DY-DX, 2*(DY-DX)        }
  2104.    @flach:
  2105.      SHL BX,1
  2106.      MOV DY_mal2,BX
  2107.      SUB BX,CX
  2108.      MOV BP,BX       {BP:=2*DY-DX}
  2109.      SUB BX,CX
  2110.      MOV DY_m_DX_mal2,BX
  2111.      INC CX          {CX:=number of pixels}
  2112.      MOV BL,Color
  2113.      MOV BH,1
  2114.      CMP Steigung,0  {steep line?  }
  2115.      JNZ @high1      {yes}
  2116.  
  2117.    @low1:            {no  }
  2118.      MOV AX,3C4h
  2119.      XCHG AX,DX
  2120.      OUT DX,AX       {select correct bitplane    }
  2121.      MOV DX,AX       {save mask to DX again      }
  2122.      MOV AL,BL       {get color of point    }
  2123.      STOSB           {draw point  }
  2124.      SHL DH,1        {compute mask for next point             }
  2125.      CMP DH,16       {still addressable with the same address? }
  2126.      JE @nextbyte1   {no, address had to be incremented by 1  }
  2127.      DEC DI          {yes, make increase of DI undone          }
  2128.    @low1b:
  2129.      OR BP,BP
  2130.      JGE @low2
  2131.      ADD BP,DY_mal2
  2132.      LOOP @low1
  2133.      JMP @raus
  2134.    @nextbyte1:
  2135.      MOV DH,BH       {restore mask to 1        }
  2136.      JMP @low1b      {rest as above  }
  2137.  
  2138.    @low2:
  2139.      ADD BP,DY_m_DX_mal2
  2140.      ADD DI,SI
  2141.      LOOP @low1
  2142.      JMP @raus
  2143.  
  2144.  
  2145.    @high1:
  2146.      MOV AX,3C4h
  2147.      XCHG AX,DX
  2148.      OUT DX,AX
  2149.      MOV DX,AX
  2150.      MOV AL,BL
  2151.    @high1b:
  2152.      OR BP,BP
  2153.      JGE @high2
  2154.      ADD BP,DY_mal2
  2155.      MOV ES:[DI],AL
  2156.      ADD DI,SI
  2157.      LOOP @high1b
  2158.      JMP @raus
  2159.  
  2160.    @high2:
  2161.      ADD BP,DY_m_DX_mal2
  2162.      SHL DH,1
  2163.      CMP DH,16
  2164.      JE @nextbyte2
  2165.      MOV ES:[DI],AL
  2166.      ADD DI,SI
  2167.      LOOP @high1
  2168.      JMP @raus
  2169.    @nextbyte2:
  2170.      MOV DH,BH
  2171.      STOSB
  2172.      ADD DI,SI
  2173.      LOOP @high1
  2174.  
  2175.    @raus:
  2176.      POP BP
  2177.    @LineReady:
  2178.   END;
  2179. END;
  2180.  
  2181. PROCEDURE BackgroundLine(x1,y1,x2,y2:INTEGER);
  2182. { in: x1,y1,x2,y2 = coordinates of two points, }
  2183. {     Color       = color (0..255)             }
  2184. {     StartVirtualX,StartVirtualY = upper left image corner           }
  2185. {out: - }
  2186. {rem: A line has been drawn between the VIRTUAL points (x1,y1) and (x2,y2) }
  2187. {     (using the color COLOR) into the background page; the routine it-   }
  2188. {     self takes care of coordinate transformations and evtl. necessary   }
  2189. {     clipping actions.                                                       }
  2190. {     The line will NOT be visible until the next animation cycle takes   }
  2191. {     place (but then, it stays permanent)! (For that reason, you normally}
  2192. {     will call this routine BEFORE calling ANIMATE, because this way, all }
  2193. {     changes will become visible (via ANIMATE) at once)                  }
  2194. {     Because BACKGNDADR is used as background page, calling this rou-    }
  2195. {     tine only makes sense when using background mode STATIC!            }
  2196. BEGIN
  2197.  Line(x1,y1,x2,y2,BACKGNDPAGE)
  2198. END;
  2199.  
  2200. FUNCTION GetPixel(x,y:INTEGER):BYTE; ASSEMBLER;
  2201. { in: x,y    = VIRTUAL pixel coordinates of the point to be read   }
  2202. {     PAGEADR= graphic page(segment) to be read from               }
  2203. {     StartVirtualX, StartVirtualY = upper left image corner       }
  2204. {out: color of the point}
  2205. {rem: If the pixel lies outside the visible window, the routine    }
  2206. {     will return "0" as the result        }
  2207. {     Attention! As PAGEADR always specifies the NOT visible gra-     }
  2208. {     phic page, this routine will read from there, too!           }
  2209. ASM
  2210.  XOR AL,AL              {preset AL with 0    }
  2211.  MOV DI,y
  2212.  SUB DI,StartVirtualY   {transform y into absolue coordinates}
  2213.  JS @offscrn
  2214.  CMP DI,YMAX
  2215.  JG @offscrn
  2216.  MOV BX,x
  2217.  SUB BX,StartVirtualX   {transform x into absolute coordinates}
  2218.  JS @offscrn
  2219.  CMP BX,XMAX
  2220.  JG @offscrn
  2221.  SHL DI,1
  2222.  MOV DI,CS:[OFFSET gadr + DI]
  2223.  {DI = Y*LINESIZE, BX = X, coordinates admissible}
  2224.  MOV AX,BX
  2225.  SHR AX,1
  2226.  SHR AX,1
  2227.  ADD DI,AX    {DI = Y*LINESIZE+(X SHR 2) }
  2228.  AND BL,3     {BL = X MOD 4 = plane to read from}
  2229.  MOV AL,4
  2230.  MOV AH,BL
  2231.  MOV DX,3CEh
  2232.  
  2233.  MOV ES,PAGEADR
  2234.  CLI
  2235.  OUT DX,AX
  2236.  MOV AL,ES:[DI]
  2237.  STI
  2238. @offscrn:
  2239. END;
  2240.  
  2241. FUNCTION BackgroundGetPixel(x,y:INTEGER):BYTE; ASSEMBLER;
  2242. { in: x,y   = VIRTUAL pixel coordinates of the point to be read   }
  2243. {     StartVirtualX, StartVirtualY = upper left image corner      }
  2244. {out: color of the point in the background page}
  2245. {rem: If the pixel lies outside the visible window, the routine   }
  2246. {     will return "0" as the result        }
  2247. {     Because BACKGNDADR is used as background page, calling      }
  2248. {     this routine only makes sense when using mode STATIC!       }
  2249. ASM
  2250.  XOR AL,AL              {preset AL with 0    }
  2251.  MOV DI,y
  2252.  SUB DI,StartVirtualY   {transform y into absolue coordinates}
  2253.  JS @offscrn
  2254.  CMP DI,YMAX
  2255.  JG @offscrn
  2256.  MOV BX,x
  2257.  SUB BX,StartVirtualX   {transform x into absolute coordinates}
  2258.  JS @offscrn
  2259.  CMP BX,XMAX
  2260.  JG @offscrn
  2261.  SHL DI,1
  2262.  MOV DI,CS:[OFFSET gadr + DI]
  2263.  {DI = Y*LINESIZE, BX = X, coordinates admissible}
  2264.  MOV AX,BX
  2265.  SHR AX,1
  2266.  SHR AX,1
  2267.  ADD DI,AX    {DI = Y*LINESIZE+(X SHR 2) }
  2268.  AND BL,3     {BL = X MOD 4 = plane to read from}
  2269.  MOV AL,4
  2270.  MOV AH,BL
  2271.  MOV DX,3CEh
  2272.  MOV ES,BACKGNDADR
  2273.  CLI
  2274.  OUT DX,AX
  2275.  MOV AL,ES:[DI]
  2276.  STI
  2277. @offscrn:
  2278. END;
  2279.  
  2280. FUNCTION PageGetPixel(x,y:INTEGER; pa:BYTE):BYTE; ASSEMBLER;
  2281. { in: x,y   = VIRTUAL pixel coordinates of the point to be read   }
  2282. {     pa    = graphic page (0..3), from which the point shall be    }
  2283. {             read out   }
  2284. {     StartVirtualX, StartVirtualY = upper left image corner      }
  2285. {out: color of the point in the background page}
  2286. {rem: If the pixel lies outside the visible window, the routine   }
  2287. {     will return "0" as the result        }
  2288. {     If you want to read from the actually VISIBLE page, then    }
  2289. {     then you must call the routine with pa=1-PAGE!                   }
  2290. {     Sensible values for "pa" are either 0 or 1 (and evtl. BACK- }
  2291. {     GNDPAGE, if you are using background mode STATIC), however, }
  2292. {     the routine doesn't check that!         }
  2293. ASM
  2294.  XOR AL,AL              {preset AL with 0    }
  2295.  MOV DI,y
  2296.  SUB DI,StartVirtualY   {transform y into absolue coordinates}
  2297.  JS @offscrn
  2298.  CMP DI,YMAX
  2299.  JG @offscrn
  2300.  MOV BX,x
  2301.  SUB BX,StartVirtualX   {transform x into absolute coordinates}
  2302.  JS @offscrn
  2303.  CMP BX,XMAX
  2304.  JG @offscrn
  2305.  SHL DI,1
  2306.  MOV DI,CS:[OFFSET gadr + DI]
  2307.  {DI = Y*LINESIZE, BX = X, coordinates admissible}
  2308.  MOV AX,BX
  2309.  SHR AX,1
  2310.  SHR AX,1
  2311.  ADD DI,AX    {DI = Y*LINESIZE+(X SHR 2) }
  2312.  AND BX,3     {BL = X MOD 4 = plane to read from; BH = 0}
  2313.  MOV AL,4
  2314.  MOV AH,BL
  2315.  MOV BL,pa    {BH=0 -> BX = graphic page}
  2316.  AND BX,3     {only pages 0..3}
  2317.  SHL BX,1
  2318.  ADD BX,OFFSET Segment_Adr-StartIndex*2
  2319.  MOV ES,[BX]
  2320.  
  2321.  CLI
  2322.  MOV DX,3CEh
  2323.  OUT DX,AX
  2324.  MOV AL,ES:[DI]
  2325.  STI
  2326. @offscrn:
  2327. END;
  2328.  
  2329.  
  2330. PROCEDURE PutPixel(x,y:INTEGER; color:Byte); ASSEMBLER;
  2331. { in: x,y    = VIRTUAL pixel coordinates of the point to be written }
  2332. {     color  = color value for the pixel to be drawn }
  2333. {     1-PAGE = graphic page to be drawn upon              }
  2334. {     StartVirtualX, StartVirtualY = upper left image corner   }
  2335. {out: - }
  2336. {rem: The point (x,y) has been transformed to absolute screen coordinates  }
  2337. {     and has been drawn (if it lies within the visible window)            }
  2338. {     The pixel will NOT automatically be overtaken into the background    }
  2339. {     image, that is: it will be visible only for one animation cycle!       }
  2340. {     (For that reason, you should call this routine AFTER calling ANIMATE }
  2341. {     because otherwise, the drawn point will vanish at once!)             }
  2342. ASM
  2343.  MOV DI,y
  2344.  SUB DI,StartVirtualY   {transform y into absolue coordinates}
  2345.  JS @offscrn
  2346.  CMP DI,YMAX
  2347.  JG @offscrn
  2348.  MOV BX,x
  2349.  SUB BX,StartVirtualX   {transform x into absolute coordinates}
  2350.  JS @offscrn
  2351.  CMP BX,XMAX
  2352.  JG @offscrn
  2353.  SHL DI,1
  2354.  MOV DI,CS:[OFFSET gadr + DI]
  2355.  {DI = Y*LINESIZE, BX = X, coordinates admissible}
  2356.  MOV AX,BX
  2357.  SHR AX,1
  2358.  SHR AX,1
  2359.  ADD DI,AX    {DI = Y*LINESIZE+(X SHR 2) }
  2360.  AND BX,3
  2361.  MOV AH,[OFFSET TranslateTab + BX]
  2362.  MOV AL,2
  2363.  MOV DX,3C4h
  2364.  
  2365.  MOV BX,1     {ES:=Segment_Adr[1-PAGE], because 1-PAGE=visible page}
  2366.  SUB BX,PAGE
  2367.  SHL BX,1
  2368.  ADD BX,OFFSET Segment_Adr-StartIndex*2
  2369.  MOV ES,[BX]
  2370.  
  2371.  CLI
  2372.  OUT DX,AX
  2373.  MOV AL,color
  2374.  STOSB
  2375.  STI
  2376. @offscrn:
  2377. END;
  2378.  
  2379. PROCEDURE BackgroundPutPixel(x,y:INTEGER; color:Byte); ASSEMBLER;
  2380. { in: x,y   = VIRTUAL pixel coordinates of the point to be written }
  2381. {     color = color value for the pixel to be drawn }
  2382. {     StartVirtualX, StartVirtualY = upper left image corner   }
  2383. {out: - }
  2384. {rem: The point (x,y) has been transformed to absolute screen coordinates and}
  2385. {     has been drawn into the background (if it is onscreen)                 }
  2386. {     The pixel will NOT be visible until the next animation cycle takes     }
  2387. {     place (but then, it remains permanent) (For that reason, you should    }
  2388. {     call this routine BEFORE calling ANIMATE. That way, evtl. changes to   }
  2389. {     the background will be visible "at once"!)                             }
  2390. {     Because BACKGNDPAGE is used as background page, calling this           }
  2391. {     routine only makes sense when using background mode STATIC!}
  2392. ASM
  2393.  MOV DI,y
  2394.  SUB DI,StartVirtualY   {transform y into absolue coordinates}
  2395.  JS @offscrn
  2396.  CMP DI,YMAX
  2397.  JG @offscrn
  2398.  MOV BX,x
  2399.  SUB BX,StartVirtualX   {transform x into absolute coordinates}
  2400.  JS @offscrn
  2401.  CMP BX,XMAX
  2402.  JG @offscrn
  2403.  SHL DI,1
  2404.  MOV DI,CS:[OFFSET gadr + DI]
  2405.  {DI = Y*LINESIZE, BX = X, coordinates admissible}
  2406.  MOV AX,BX
  2407.  SHR AX,1
  2408.  SHR AX,1
  2409.  ADD DI,AX    {DI = Y*LINESIZE+(X SHR 2) }
  2410.  AND BX,3
  2411.  MOV AH,[OFFSET TranslateTab + BX]
  2412.  MOV AL,2
  2413.  MOV DX,3C4h
  2414.  MOV ES,BACKGNDADR
  2415.  CLI
  2416.  OUT DX,AX
  2417.  MOV AL,color
  2418.  STOSB
  2419.  STI
  2420. @offscrn:
  2421. END;
  2422.  
  2423. PROCEDURE PagePutPixel(x,y:INTEGER; color,pa:Byte); ASSEMBLER;
  2424. { in: x,y    = VIRTUAL pixel coordinates of the point to be written }
  2425. {     color  = color value for the pixel to be drawn }
  2426. {     pa     = graphic page (0..3) to be drawn upon                 }
  2427. {     PAGEADR= graphic page(segment) to be drawn upon               }
  2428. {     StartVirtualX, StartVirtualY = upper left image corner   }
  2429. {out: - }
  2430. {rem: The point (x,y) has been transformed to absolute screen coordinates  }
  2431. {     and has been drawn (if it lies within the visible window)            }
  2432. {     If you want to draw at the actually VISIBLE graphic page     }
  2433. {     then you must call the routine with pa=1-PAGE!                    }
  2434. {     Again, the drawn pixel will _NOT_ automatically be taken     }
  2435. {     over into the background image, that is: it will be visible  }
  2436. {     only until the next animation cycle (=till calling ANIMATE)    }
  2437. {     (For that reason, you should call this routine AFTER         }
  2438. {     calling ANIMATE, because otherwise, your drawn pixel will    }
  2439. {     vanish at once!)                                             }
  2440. {     Sensible values for "pa" are either 0 or 1 (and evtl. BACK-  }
  2441. {     GNDPAGE, if you are using background mode STATIC), however,  }
  2442. {     the routine doesn't check that!         }
  2443. ASM
  2444.  MOV DI,y
  2445.  SUB DI,StartVirtualY   {transform y into absolue coordinates}
  2446.  JS @offscrn
  2447.  CMP DI,YMAX
  2448.  JG @offscrn
  2449.  MOV BX,x
  2450.  SUB BX,StartVirtualX   {transform x into absolute coordinates}
  2451.  JS @offscrn
  2452.  CMP BX,XMAX
  2453.  JG @offscrn
  2454.  SHL DI,1
  2455.  MOV DI,CS:[OFFSET gadr + DI]
  2456.  {DI = Y*LINESIZE, BX = X, coordinates admissible}
  2457.  MOV AX,BX
  2458.  SHR AX,1
  2459.  SHR AX,1
  2460.  ADD DI,AX    {DI = Y*LINESIZE+(X SHR 2) }
  2461.  AND BX,3
  2462.  MOV AH,[OFFSET TranslateTab + BX]
  2463.  MOV AL,2
  2464.  MOV DX,3C4h
  2465.  MOV BL,pa    {BH=0 -> BX=graphic page}
  2466.  SHL BX,1
  2467.  ADD BX,OFFSET Segment_Adr+StartIndex*2
  2468.  MOV ES,[BX]
  2469.  
  2470.  CLI
  2471.  OUT DX,AX
  2472.  MOV AL,color
  2473.  STOSB
  2474.  STI
  2475. @offscrn:
  2476. END;
  2477.  
  2478.  
  2479. PROCEDURE OutTextXY(x,y:INTEGER; pa:BYTE; s:STRING);
  2480. { in: (x,y)  = (virtual) starting coordinates for the text to be written}
  2481. {     s      = textstring to be displayed                             }
  2482. {     pa     = graphic page where the text shall be written         }
  2483. {     GraphTextColor=color for text                                      }
  2484. {     GraphTextBackground=color to be used for text background; if  }
  2485. {            this value equals GraphTextColor, only the text-pixels }
  2486. {             themselves will be drawn while the surrounding ones   }
  2487. {             don't change (=normal behaviour of TP's OutText-procs)  }
  2488. {     GraphTextOrientation="vertical" or "horizontal"               }
  2489. {     StartVirtualX,StartVirtualY = upper left image corner         }
  2490. {out: text has been written to the screen                           }
  2491. VAR z,b,bit,i:BYTE;
  2492.     data:Fontchar;
  2493. BEGIN
  2494.  IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE)
  2495.   THEN BEGIN
  2496.         Error:=Err_InvalidPageNumber;
  2497.         exit
  2498.        END;
  2499.  FOR i:=1 TO Length(s) DO
  2500.  BEGIN
  2501.   data:=FontData[ord(s[i])];
  2502.   FOR z:=0 TO FontHeight-1 DO
  2503.    BEGIN
  2504.     b:=data[z];
  2505.     FOR bit:=0 TO FontWidth-1 DO
  2506.      IF b and FontMask[bit]<>0
  2507.       THEN PagePutPixel(x+bit,y+z,GraphTextColor,pa)
  2508.       ELSE IF (GraphTextColor<>GraphTextBackground)
  2509.             THEN PagePutPixel(x+bit,y+z,GraphTextBackground,pa);
  2510.    END;
  2511.   IF GraphTextOrientation=horizontal
  2512.    THEN INC(x,FontWidth)
  2513.    ELSE INC(y,FontHeight);
  2514.  END;
  2515. END;
  2516.  
  2517. PROCEDURE BackgroundOutTextXY(x,y:INTEGER; s:STRING);
  2518. {rem: Functionally equivalent to OutTextXY(), but the text will be    }
  2519. {     written to the background page instead of page PAGEADR!                 }
  2520. {     Because BACKGNDADR is used as background page, calling          }
  2521. {     this routine only makes sense when using mode STATIC! }
  2522. VAR z,b,bit,i:BYTE;
  2523.     data:Fontchar;
  2524. BEGIN
  2525.  FOR i:=1 TO Length(s) DO
  2526.  BEGIN
  2527.   data:=FontData[ord(s[i])];
  2528.   FOR z:=0 TO FontHeight-1 DO
  2529.    BEGIN
  2530.     b:=data[z];
  2531.     FOR bit:=0 TO FontWidth-1 DO
  2532.      IF b and FontMask[bit]<>0
  2533.       THEN BackgroundPutPixel(x+bit,y+z,GraphTextColor)
  2534.       ELSE IF (GraphTextColor<>GraphTextBackground)
  2535.             THEN BackgroundPutPixel(x+bit,y+z,GraphTextBackground);
  2536.    END;
  2537.   IF GraphTextOrientation=horizontal
  2538.    THEN INC(x,FontWidth)
  2539.    ELSE INC(y,FontHeight);
  2540.  END;
  2541. END;
  2542.  
  2543.  
  2544. FUNCTION Hitdetect(s1,s2:INTEGER):BOOLEAN; ASSEMBLER;
  2545. { in: s1,s2 = sprite position numbers of two sprites}
  2546. {     SpriteN[s1],SpriteX[s1],SpriteY[s1] = sprite data of sprite s1     }
  2547. {     SpriteN[s2],SpriteX[s2],SpriteY[s2] = sprite data of sprite s2     }
  2548. {out: TRUE/FALSE for "sprites collide"/"sprites do not collide"          }
  2549. {rem: This check is pixel-precise and doesn't depend on the sprites being}
  2550. {     visible (=onscreen) or not!                                        }
  2551. {     inactive sprites (SpriteN[s?]=0) cannot collide                    }
  2552. {     A sprite can't collide with itself, (thus:  s1=s2 -> FALSE)        }
  2553. ASM
  2554.      MOV SI,s1               {get 1st parameter s1 from stack}
  2555.      MOV DI,s2               {get 2nd parameter s2 from stack}
  2556.      CMP SI,DI
  2557.      JE  @NOHIT1             {sprite can't collide with itself     }
  2558.      SHL SI,1
  2559.      mov cx,[SI + OFFSET SpriteN]
  2560.      jcxz @NOHIT1            {sprite <>0, that is: sprite active?}
  2561.      SHL DI,1
  2562.      MOV BX,[DI + OFFSET SpriteN]
  2563.      OR  BX,BX               {dto. for other sprite   }
  2564.      JNE @PRUEF2
  2565.    @NOHIT1:
  2566.      JMP @NOHIT7             {inactive sprites can't collide     }
  2567.                              {either -> return FALSE             }
  2568. {here: SI (DI) = pointer to 1. (2.) sprite in ?YWRTD[..],}
  2569. {      CX (BX) = spritenumber of sprite 1 (2)            }
  2570. {(a bit later, DS (ES) becomes segment addr. of sprite data of spr. 1 (2) )}
  2571.    @PRUEF2:
  2572.      MOV AX,[SI + OFFSET SpriteY]
  2573.      MOV DX,[DI + OFFSET SpriteY]
  2574.      mov si,[SI + OFFSET SpriteX]  {SI=x1}
  2575.      mov di,[DI + OFFSET SpriteX]  {DI=x2}
  2576.      shl bx,1                      {BX=Spritenumber2*2}
  2577.      mov es,[BX + OFFSET SPRITEAD] {ES=segment of 2nd sprite's data}
  2578.      mov bx,cx                     {(CX=spritenumber1)}
  2579.      shl bx,1                      {BX=spritenumber1*2}
  2580.      MOV ds,[BX + OFFSET SPRITEAD]
  2581.  
  2582.      mov [y1],ax
  2583.      mov [y2],dx
  2584.      sub dx,ax
  2585.      mov CS:WORD PTR @y2_y1+1,dx
  2586.      mov [x1],si
  2587.      mov [x2],di
  2588.      mov dx,di
  2589.      sub dx,si
  2590.      mov CS:WORD PTR @x2_x1+1,dx
  2591.      mov ax,es:[Left]              {AX=pointer to left boundary data}
  2592.      mov CS:WORD PTR @lirand2+1,ax
  2593.      mov ax,es:[Right]             {AX=pointer to right boundary data}
  2594.      mov CS:WORD PTR @rerand2+1,ax
  2595.      mov ax,es:[Top]               {AX=pointer to upper boundary data}
  2596.      mov CS:WORD PTR @orand2+1,ax
  2597.      mov ax,es:[Bottom]            {AX=pointer to lower boundary data}
  2598.      mov CS:WORD PTR @urand2+1,ax
  2599.      mov ax,es:[Breite]            {AX=max. width in groups of 4 }
  2600.      shl al,1
  2601.      shl al,1
  2602.      mov CS:WORD PTR @breite2+1,ax {*4 = width in points  }
  2603.      mov ax,es:[Hoehe]
  2604.      mov CS:WORD PTR @hoehe2+1,ax  {height of sprite2 in points }
  2605.  
  2606.      MOV AX,[Left]                 {AX=pointer to left boundary data}
  2607.      MOV CS:WORD PTR @LIRAND1+1,AX
  2608.      MOV AX,[Right]                {AX=pointer to right boundary data}
  2609.      MOV CS:WORD PTR @RERAND1+1,AX
  2610.      MOV AX,[Top]                  {AX=pointer to upper boundary data}
  2611.      MOV CS:WORD PTR @ORAND1+1,AX
  2612.      MOV AX,[Bottom]               {AX=pointer to lower boundary data}
  2613.      MOV CS:WORD PTR @URAND1+1,AX
  2614.      MOV BX,[Breite]               {BX=max. width in groups of 4 }
  2615.      SHL BX,1
  2616.      SHL BX,1                      {*4 = width in points  }
  2617.      MOV CS:WORD PTR @BREITE1+2,BX
  2618.  
  2619.      lea bx,[si+bx-1]              {BX:=x1+breite1-1  (=x1last)}
  2620.    @breite2:
  2621.      mov bp,1234h                  {dummy value}
  2622.      mov cx,bp                     {CX=breite2 will be needed later again  }
  2623.      lea bp,[di+bp-1]              {BP:=x2+breite2-1  (=x2last)}
  2624.      cmp bx,bp
  2625.      jle @noex1
  2626.      mov bp,bx
  2627.    @noex1:                         {here: BP=max(x1last,x2last)  (=maxx)}
  2628.      cmp si,di
  2629.      jle @X1_klgl_X2
  2630.      xchg si,di
  2631.    @X1_klgl_X2:                    {here: SI=min(x1,x2)  (=minx)}
  2632.      stc
  2633.      sbb si,bp                     {SI:=minx-maxx-1=-(maxx-minx+1)}
  2634.    @breite1:
  2635.      add cx,1234h                  {(dummy value) CX:=breite1+breite2}
  2636.      add cx,si                     {CX:=breite1+breite2-(maxx-minx+1)}
  2637.      dec cx           {CX:=breite1+breite2-(maxx-minx+1)-1  (=ueberlappx-1)}
  2638.      js @NOHIT2                    {no collision, if ueberlappx<=0  }
  2639.      mov [ueberlappx_1],cx
  2640.  
  2641.      mov ax,[Hoehe]
  2642.      mov bx,ax                     {BX:=hoehe1}
  2643.      mov di,[y1]                   {DI:=y1}
  2644.      add ax,di                     {AX:=y1+hoehe1}
  2645.      dec ax                        {AX:=y1+hoehe1-1  (=y1last)}
  2646.    @hoehe2:
  2647.      mov si,1234h
  2648.      mov dx,[y2]
  2649.      add dx,si                     {DX:=y2+hoehe2}
  2650.      dec dx                        {DX:=y2+hoehe2-1  (=y2last)}
  2651.      cmp ax,dx
  2652.      jge @noex2
  2653.      mov ax,dx
  2654.    @noex2:                         {here: AX=max(y1last,y2last)  (=maxy)}
  2655.      mov dx,[y2]
  2656.      cmp di,dx                     {(DI=y1)}
  2657.      jle @noex3
  2658.      mov di,dx
  2659.    @noex3:                         {here: DI=min(y1,y2)  (=miny)}
  2660.      sub di,ax                     {DI:=miny-maxy=-(maxy-miny)}
  2661.      lea ax,[bx+si-2]              {AX:=hoehe1+hoehe2-2}
  2662.      add ax,di          {AX:=hoehe1+hoehe2-(maxy-miny+1)-1  (=ueberlappy-1)}
  2663.      js @NOHIT2                    {no collision, if ueberlappy<=0  }
  2664.      mov [ueberlappy_1],ax
  2665.  
  2666. {here: AX=ueberlappy-1, CX=ueberlappx-1}
  2667.    @x2_x1:
  2668.      mov dx,1234h                  {dummy value}
  2669.      xor bx,bx                     {from now on: BX=0!}
  2670.      or dx,dx
  2671.      js @X2_X1_kl_0                {if x2-x1>=0 then...}
  2672.      mov [hit2xfirst],bx           {...hit2xfirst:=0}
  2673.      mov [hit1xfirst],dx           {...hit1xfirst:=x2-x1}
  2674.      jmp @Yhits    {SHORT}
  2675.  
  2676. {jump-rail for NOHIT (this is a good place)  }
  2677.    @NOHIT2:
  2678.      JMP @NOHIT7
  2679.  
  2680. {now back at "normal" program  }
  2681.    @X2_X1_kl_0:                    {else (x2-x1<0)...}
  2682.      mov [hit1xfirst],bx           {...hit1xfirst:=0}
  2683.      neg dx                        {DX:=x1-x2}
  2684.      mov [hit2xfirst],dx           {...hit2xfirst:=x1-x2}
  2685.  
  2686.    @Yhits:                         {here: AX=ueberlappy-1}
  2687.    @y2_y1:
  2688.      mov dx,1234h                  {dummy value}
  2689.      or dx,dx
  2690.      js @Y2_Y1_kl_0                {if y2-y1>=0 then...}
  2691.      mov [hit2yfirst],bx           {...hit2yfirst:=0}
  2692.      mov [hit1yfirst],dx           {...hit1yfirst:=y2-y1}
  2693.      jmp @iterate  {SHORT}
  2694.    @Y2_Y1_kl_0:                    {else (y2-y1<0)...}
  2695.      mov [hit1yfirst],bx           {...hit1yfirst:=0}
  2696.      neg dx                        {DX:=y1-y2}
  2697.      mov [hit2yfirst],dx           {...hit2yfirst:=y1-y2}
  2698.  
  2699. {Now check the overlapping rows and columns more closely by iteration:   }
  2700.    @iterate:
  2701.      mov cx,[ueberlappy_1]         {number of rows -1 to compare          }
  2702.      shl cx,1                      {*2, because word-sized!}
  2703.    @lirand1:
  2704.      mov si,1234h                  {dummy value}
  2705.    @lirand2:
  2706.      mov di,1234h                  {dummy value}
  2707.    @rerand1:
  2708.      mov bx,1234h                  {dummy value}
  2709.    @rerand2:
  2710.      mov bp,1234h                  {dummy value}
  2711.      sub bx,si                     {BX:=rerand1-lirand1}
  2712.      sub bp,di                     {BP:=rerand2-lirand2}
  2713.      mov ax,[hit1yfirst]
  2714.      shl ax,1
  2715.      add si,ax                  {SI:=1st row where sprite1 overlaps sprite2  }
  2716.      mov ax,[hit2yfirst]
  2717.      shl ax,1
  2718.      add di,ax                  {DI:=1st row where sprite2 overlaps sprite1  }
  2719.      add si,cx                  {dto., last row    }
  2720.      add di,cx
  2721.    @one_line:
  2722.      mov ax,[si]                   {DS:AX:=x1li[row]  }
  2723.      mov dx,es:[di]                {ES:DX:=x2li[row]  }
  2724.      add ax,[x1]                   {AX:=x1li[row]+x1  (=c)  }
  2725.      add dx,[x2]                   {DX:=x2li[row]+x2  (=d)  }
  2726.      cmp ax,dx
  2727.      jge @C_grgl_D
  2728.      mov ax,dx
  2729.    @C_grgl_D:                      {here: AX=max(c,d)}
  2730.      mov cx,[si+bx]                {DS:CX:=x1re[row]  }
  2731.      mov dx,es:[di+bp]             {ES:DX:=x2re[row]  }
  2732.      add cx,[x1]                   {CX:=x1re[row]+x1  (=a)  }
  2733.      add dx,[x2]                   {DX:=x2re[row]+x2  (=b)  }
  2734.      cmp cx,dx
  2735.      jle @A_klgl_B
  2736.      mov cx,dx
  2737.    @A_klgl_B:                      {here: CX=min(a,b)}
  2738.      cmp cx,ax                     {min(a,b)>=max(c,d) ?}
  2739.      jge @found_Xhit               {yes: collision in X-direction found!}
  2740.      dec si                        {next row (-> word-sized values!)}
  2741.      dec si
  2742.      dec di
  2743.      dec di
  2744.      dec WORD PTR [ueberlappy_1]
  2745.      jns @one_line
  2746. {no collision in X-direction -> no collision at all!   }
  2747.      jmp @NOHIT7
  2748.  
  2749. {otherwise: collision in X-direction, now check Y-dir. also (as above) and  }
  2750. {report "collision!" only, if there is at least 1 collision in Y-dir., too  }
  2751.    @found_Xhit:
  2752.      mov cx,[ueberlappx_1]         {number of columns -1 to compare        }
  2753.      shl cx,1                      {*2, because word-sized!}
  2754.    @orand1:
  2755.      mov si,1234h                  {dummy value}
  2756.    @orand2:
  2757.      mov di,1234h                  {dummy value}
  2758.    @urand1:
  2759.      mov bx,1234h                  {dummy value}
  2760.    @urand2:
  2761.      mov bp,1234h                  {dummy value}
  2762.      sub bx,si                     {BX:=urand1-orand1}
  2763.      sub bp,di                     {BP:=urand2-orand2}
  2764.      mov ax,[hit1xfirst]
  2765.      shl ax,1                      {*2, because word-sized!}
  2766.      add si,ax                     {SI:=orand1+2*hit1xfirst}
  2767.      mov ax,[hit2xfirst]
  2768.      shl ax,1                      {*2, because word-sized!}
  2769.      add di,ax                     {DI:=orand2+2*hit2xfirst}
  2770.      add si,cx
  2771.      add di,cx
  2772.    @one_column: mov ax,[si]        {AX:=y1ob[column]}
  2773.      cmp ax,16000                  {dummy value for "empty column"?}
  2774.      je @next_column               {yes, thus: surely no collision  }
  2775.      mov dx,es:[di]                {DX:=y2ob[column]}
  2776.      cmp dx,16000                  {check 2nd sprite too: "empty column"? }
  2777.      je @next_column               {yes, no collision}
  2778.      add ax,[y1]                   {AX:=y1ob+y1  (=c)}
  2779.      add dx,[y2]                   {DX:=y2ob+y2  (=d)}
  2780.      cmp ax,dx
  2781.      jge @C_grgl_D2
  2782.      mov ax,dx
  2783.    @C_grgl_D2:                     {here: AX=max(c,d)}
  2784.      mov cx,[si+bx]                {DS:CX:=y1un[column]}
  2785.      mov dx,es:[di+bp]             {ES:DX:=y2un[column]}
  2786.      add cx,[y1]                   {CX:=y1un+y1  (=a)}
  2787.      add dx,[y2]                   {DX:=y2un+y2  (=b)}
  2788.      cmp cx,dx
  2789.      jle @A_klgl_B2
  2790.      mov cx,dx
  2791.    @A_klgl_B2:                     {here: CX=min(a,b)}
  2792.      cmp cx,ax                     {min(a,b)>=max(c,d) ?}
  2793.      jge @HIT2                     {yes: collision detected!}
  2794.    @next_column:
  2795.      dec si                        {no, next column (-> word-sized values!)}
  2796.      dec si
  2797.      dec di
  2798.      dec di
  2799.      dec WORD PTR [ueberlappx_1]
  2800.      jns @one_column
  2801.  
  2802.    @NOHIT7:
  2803.      XOR AX,AX                     {return 0 = FALSE as result         }
  2804.      JMP @TREFF_END  {SHORT}
  2805.    @HIT2:
  2806.      MOV AX,1                      {return 1 = TRUE as result         }
  2807.  
  2808.    @TREFF_END:
  2809.    {$IFOPT G+}
  2810.      mov bp,sp                     {only necessary for compiler switch G+!}
  2811.    {$ENDIF}
  2812.      mov dx,seg @DATA              {else, BP will be restored by TP itself}
  2813.      mov ds,dx
  2814. END;
  2815.  
  2816.  
  2817. PROCEDURE Animate;
  2818. { in: PAGEADR = actual graphicpage(address) on which to draw upon            }
  2819. {     BACKGNDADR = background page(address)   }
  2820. {     BACKGROUNDMODE = STATIC/SCROLLING for solid/scrollable background      }
  2821. {     SpriteN[] = spritenumber of sprite to be displayed    }
  2822. {     SpriteX[],SpriteY[] = their (virtual) coordinates                 }
  2823. {     StartVirtualX,StartVirtualY = upper left image corner     }
  2824. {     (PAGE = actually displayed graphic page)  }
  2825. {out: PAGE = 0/1, if PAGE has been 1/0, respectively }
  2826. {     PAGEADR = new, actual graphic page(address)    }
  2827. {rem: Animate erases the old contents of the page (using the background page }
  2828. {     information), draws all visible sprites, synchronizes to the display-  }
  2829. {     enable signal and switches the display to that now completed page      }
  2830. VAR offsetXTiles,offsetYTiles,offsetXPix,offsetYPix:INTEGER;
  2831.     leftcut,rightcut,topcut,bottomcut,tiles:WORD;
  2832.     x,y,xpix,ypix,xtil,ytil,actindex,randindex,index:INTEGER;
  2833.     offscreenFlag:BYTE;
  2834.     yt,xt:INTEGER;
  2835. BEGIN
  2836.  ASM
  2837.     CLD
  2838.     {first copy the background picture to the actual graphic page:}
  2839.     CMP BackgroundMode,STATIC   {which background mode?   }
  2840.     JE @static_bckgnd
  2841.     JMP @scrolling_bckgnd
  2842.  
  2843.   @static_bckgnd:
  2844.     MOV AX,0F02h                {write to all 4 planes simultaneously  }
  2845.     MOV DX,3C4h
  2846.     OUT DX,AX
  2847.     MOV AX,4105h                {choose write mode 1   }
  2848.     MOV DX,3CEh
  2849.     OUT DX,AX
  2850.  
  2851.     MOV ES,PAGEADR              {fill graphic page with background pattern}
  2852.     MOV DS,BACKGNDADR
  2853.     XOR SI,SI
  2854.     MOV DI,SI
  2855.     MOV CX,PAGESIZE
  2856.  
  2857.     REP MOVSB
  2858.  
  2859.     MOV AX,SEG @DATA
  2860.     MOV DS,AX
  2861.     MOV AX,4005h          {select write mode 0}
  2862.     MOV DX,3CEh
  2863.     OUT DX,AX
  2864.  
  2865.     JMP @Sprites_zeichnen
  2866.  
  2867.   {---------------------------------}
  2868.  
  2869.   @scrolling_bckgnd:      {now: create background image from tiles        }
  2870.     MOV AX,StartVirtualY
  2871.     MOV BX,AX             {AX=BX=StartVirtualY}
  2872.     SUB AX,BackY1
  2873.     ADD AX,15             {offsetYTiles:=(StartVirtualY-BackY1+15) DIV 16}
  2874.     SAR AX,1
  2875.     SAR AX,1
  2876.     SAR AX,1
  2877.     SAR AX,1
  2878.     MOV offsetYTiles,AX
  2879.     MOV ytil,AX           {ytil:=offsetYTiles}
  2880.     DEC AX
  2881.     IMUL XTiles
  2882.     MOV actIndex,AX       {actIndex:=(ytil-1)*XTiles, "+xtil" comes later  }
  2883.  
  2884.     MOV AX,16
  2885.     SUB AX,BX             {BX=StartVirtualY}
  2886.     AND AX,$F
  2887.     MOV ypix,AX           {ypix:=(16-StartVirtualY) AND $F}
  2888.     SUB AX,200
  2889.     AND AX,$F
  2890.     MOV bottomcut,AX      {bottomcut:=(ypix-200) AND $F}
  2891.  
  2892.     AND BX,$F             {offsetYPix:=topcut:=StartVirtualY AND $F}
  2893.     MOV topcut,BX
  2894.     MOV offsetYPix,BX
  2895.  
  2896.     MOV AX,StartVirtualX
  2897.     MOV BX,AX             {AX=BX=StartVirtualX}
  2898.     SUB AX,BackX1
  2899.     ADD AX,15             {offsetXTiles:=(StartVirtualX-BackX1+15) DIV 16}
  2900.     SAR AX,1
  2901.     SAR AX,1
  2902.     SAR AX,1
  2903.     SAR AX,1
  2904.     MOV offsetXTiles,AX
  2905.     MOV xtil,AX           {xtil:=offsetXTiles}
  2906.     ADD actIndex,AX       {actIndex:=(ytil-1)*XTiles+xtil}
  2907.  
  2908.     MOV AX,16
  2909.     SUB AX,BX             {BX=StartVirtualX}
  2910.     AND AX,$F
  2911.     MOV xpix,AX           {xpix=rightcut:=(16-StartVirtualX) AND $F}
  2912.     MOV rightcut,AX
  2913.  
  2914.     AND BX,$F             {offsetXPix:=leftcut:=StartVirtualX AND $F}
  2915.     MOV leftcut,BX
  2916.     MOV offsetXPix,BX
  2917.  
  2918.     MOV AX,(XMAX+1)/16-1
  2919.     SUB BL,1              {C=1, if leftcut=0  }
  2920.     ADC AX,0
  2921.     MOV tiles,AX          {tiles:=19+ord(leftcut=0)}
  2922.  
  2923.  
  2924.     CMP topcut,0          {if topcut=0, the upper tile row doesn't}
  2925.     JE @do_innertiles     {need to be drawn separately                }
  2926.  
  2927.     {top most tile row:}
  2928.     MOV DX,xtil
  2929.     MOV xt,DX
  2930.     MOV AX,ytil
  2931.     DEC AX
  2932.     MOV yt,AX
  2933.     MOV CL,1
  2934.     JS @offscreen
  2935.     CMP AX,YTiles
  2936.     JAE @offscreen
  2937.     DEC CL
  2938.   @offscreen:  {CL=0/1 for offscreenFlag=false/true=(yt<0) OR (yt>=YTiles) }
  2939.     MOV offscreenFlag,CL
  2940.  
  2941.     CMP leftcut,0          {if leftcut=0, the upper left corner does}
  2942.     JE @do_upperinnertiles {not need to be drawed separately        }
  2943.  
  2944.     {draw upper left corner-tile:  }    {CL=offscreenFlag, DX=xt}
  2945.     XOR SI,SI   {determine tile index:}
  2946.     DEC CL      {IF offscreenFlag OR (xt-1<0) OR (xt-1>=XTiles) }
  2947.     JZ @go1     { THEN index:=0 ELSE index:=actIndex}
  2948.     DEC DX
  2949.     JS @go1
  2950.     CMP DX,XTiles
  2951.     JAE @go1
  2952.     MOV SI,actIndex  {=yt*XTiles+(xt-1)}
  2953.   @go1:
  2954.  
  2955.        {PROCEDURE DrawUpperLeftTile(leftcut,topcut:INTEGER; index:WORD);}
  2956.        { in: leftcut = number of left tile-columns to be cut off     }
  2957.        {     topcut  = dto., at the top}
  2958.        {     SI = index = tile number}
  2959.        {out: tile has been drawn at (0,0) on the actual page PAGEADR    }
  2960.        {rem: tile has been clipped at its left and top accordingly}
  2961.        {     leftcut must lie in the range 0..15, 16 is allowed (but senseless)   }
  2962.        {     topcut  must lie in the range 0..15, 16 is not allowed}
  2963.        MOV AH,[OFFSET BackTile +SI]  {AH:=BackTile[index]}
  2964.        XOR AL,AL
  2965.  
  2966.        SHR AH,1    {AX:=tile*64 =tile SHL 6 =(tile SHL 8) SHR 2      }
  2967.        RCR AL,1    {therefore: set AH:=tile and afterwards, shift AX }
  2968.        SHR AH,1    {2 bits to the right!}
  2969.        RCR AL,1
  2970.        MOV SI,topcut
  2971.        MOV CX,16
  2972.        SUB CX,SI   {CX:=16-topcut = number of rows to draw     }
  2973.        SHL SI,1
  2974.        SHL SI,1    {add 4 bytes to the (offset part of the)    }
  2975.        ADD SI,AX   {source addr. in page 3 for each cut off top-row}
  2976.  
  2977.        XOR DI,DI   {the first dest. addr. is DI:=0*LINESIZE+(0 div 4)=0 }
  2978.  
  2979.        MOV AX,leftcut
  2980.        MOV BX,AX   {have a copy of leftcut in BX       }
  2981.        SHR AX,1
  2982.        SHR AX,1
  2983.        ADD SI,AX   {increment SI by cutoff (=leftcut div 4) bytes   }
  2984.  
  2985.        {Now there is no further variable on stack, so BP can be used   }
  2986.        {for other purposes!}
  2987.        PUSH BP     {will be needed when leaving the procedure! }
  2988.        MOV BP,16+3
  2989.        SUB BP,BX   {BP:=16+3-leftcut, because the number of bytes per row  }
  2990.                    {of plane i is computed by (16+3-i-leftcut) SHR 2 }
  2991.  
  2992.  
  2993.        MOV ES,PAGEADR    {(segment part of) dest. address is active page}
  2994.        MOV DS,SCROLLADR  {(segment part of) source addr. is page SCROLLPAGE}
  2995.  
  2996.        MOV DX,3CEh
  2997.        AND BL,3
  2998.        MOV AH,BL   {AH:=leftcut mod 4}
  2999.        MOV AL,4
  3000.  
  3001.        JNE @mode0a {only if leftcut mod 4=0 is it possible to use WriteMode1      }
  3002.  
  3003.        {--- short-cut possible, using write mode1: DX=3CEh, BP=16+3-leftcut   }
  3004.        {    BP being "3 units to big" doesn't matter, because this code will  }
  3005.        {    only be run if leftcut mod 4=0, thus 16+3-leftcut=...11b, the     }
  3006.        {    "11b" will be cut off while shifting right, anyway!    }
  3007.        MOV AX,4105h
  3008.        OUT DX,AX       {choose write mode 1}
  3009.        MOV DX,3C4h
  3010.        MOV AX,0F02h    {work on all 4 planes simultaneously  }
  3011.        OUT DX,AX
  3012.  
  3013.        SHR BP,1        {use BP directly    }
  3014.        SHR BP,1        {BP:=bytes_per_plane3_row }
  3015.  
  3016.        MOV AX,LINESIZE {adjustment for row target addresses    }
  3017.        SUB AX,BP       { := LINESIZE-bytes_per_plane3_row }
  3018.        MOV DX,4        {adjustment for source addresses of the tiles}
  3019.        SUB DX,BP       { := 4-bytes_per_plane3_row }
  3020.  
  3021.                        {don't save source- and destination addresses}
  3022.        MOV BX,CX       {BX:=row counter  }
  3023.      @eineZeile4a1:
  3024.        MOV CX,BP       {CX:=bytes_per_plane0_row }
  3025.        REP MOVSB       {move data of one row  }
  3026.        ADD DI,AX       {set DI to next destination row  }
  3027.        ADD SI,DX       {set SI to next tile's source addr.   }
  3028.        DEC BX
  3029.        JNZ @eineZeile4a1
  3030.  
  3031.        {--- short cut has been taken: thus, reset write mode 0!     }
  3032.        MOV AX,4005h
  3033.        MOV DX,3CEh
  3034.        OUT DX,AX
  3035.        {---}
  3036.        JMP @UpperLeftTileDone
  3037.  
  3038.      @mode0a:
  3039.        OUT DX,AX       {choose plane from which to read}
  3040.        PUSH AX         {and preserve it for later}
  3041.  
  3042.        MOV DX,3C4h
  3043.        MOV AX,0102h    {choose write plane 0  }
  3044.        OUT DX,AX
  3045.  
  3046.        MOV BX,BP
  3047.        SHR BX,1
  3048.        SHR BX,1        {BX:=bytes_per_plane0_row = (16+3-leftcut) DIV 4 }
  3049.  
  3050.        MOV AX,LINESIZE {adjustment for row target addresses    }
  3051.        SUB AX,BX       { := LINESIZE-bytes_per_plane0_row }
  3052.        MOV DX,4        {adjustment for source addresses of the tiles}
  3053.        SUB DX,BX       { := 4-bytes_per_plane0_row }
  3054.  
  3055.        PUSH SI         {save source- and destination address for other planes}
  3056.        PUSH DI
  3057.        PUSH BP         {save BP  }
  3058.        PUSH CX         {CX = save row counter    }
  3059.        MOV BP,CX       {BP:=row counter  }
  3060.      @eineZeile1a:
  3061.        MOV CX,BX       {CX:=bytes_per_plane0_row }
  3062.        REP MOVSB       {move data of one row  }
  3063.        ADD DI,AX       {set DI to next destination row  }
  3064.        ADD SI,DX       {set SI to next tile's source addr.   }
  3065.        DEC BP
  3066.        JNZ @eineZeile1a
  3067.        POP CX
  3068.        POP BP
  3069.        POP DI
  3070.        POP SI
  3071.  
  3072.        MOV DX,3C4h
  3073.        MOV AX,0202h    {choose write plane 1  }
  3074.        OUT DX,AX
  3075.  
  3076.        MOV DX,3CEh     {next read plane:   }
  3077.        POP AX
  3078.        INC AH
  3079.        AND AH,3        {increment by 1 MOD 4}
  3080.        JNE @nowrap1a
  3081.        INC SI          {plane 0 follows after plane 3 again, but}
  3082.                        {the source address has increased by 1 byte }
  3083.      @nowrap1a:
  3084.        OUT DX,AX
  3085.        PUSH AX
  3086.  
  3087.  
  3088.        DEC BP          {BP:=16+2-leftcut}
  3089.        MOV BX,BP
  3090.        SHR BX,1
  3091.        SHR BX,1        {BX:=bytes_per_plane1_row }
  3092.  
  3093.        MOV AX,LINESIZE {adjustment for row target addresses    }
  3094.        SUB AX,BX       { := LINESIZE-bytes_per_plane1_row }
  3095.        MOV DX,4        {adjustment for source addresses of the tiles}
  3096.        SUB DX,BX       { := 4-bytes_per_plane1_row }
  3097.  
  3098.        PUSH SI         {save source- and destination address for other planes}
  3099.        PUSH DI
  3100.        PUSH BP         {save BP  }
  3101.        PUSH CX         {save row counter    }
  3102.        MOV BP,CX       {BP:=row counter  }
  3103.      @eineZeile2a:
  3104.        MOV CX,BX       {CX:=bytes_per_plane0_row }
  3105.        REP MOVSB       {move data of one row  }
  3106.        ADD DI,AX       {set DI to next destination row  }
  3107.        ADD SI,DX       {set SI to next tile's source addr.   }
  3108.        DEC BP
  3109.        JNZ @eineZeile2a
  3110.        POP CX
  3111.        POP BP
  3112.        POP DI
  3113.        POP SI
  3114.  
  3115.        MOV DX,3C4h
  3116.        MOV AX,0402h    {choose write plane 2  }
  3117.        OUT DX,AX
  3118.  
  3119.        MOV DX,3CEh     {next read plane:   }
  3120.        POP AX
  3121.        INC AH
  3122.        AND AH,3        {increment by 1 MOD 4}
  3123.        JNE @nowrap2a
  3124.        INC SI          {plane 0 follows after plane 3 again, but}
  3125.                        {the source address has increased by 1 byte }
  3126.      @nowrap2a:
  3127.        OUT DX,AX
  3128.        PUSH AX
  3129.  
  3130.  
  3131.        DEC BP          {BP:=16+1-leftcut}
  3132.        MOV BX,BP
  3133.        SHR BX,1
  3134.        SHR BX,1        {BX:=bytes_per_plane2_row }
  3135.  
  3136.        MOV AX,LINESIZE {adjustment for row target addresses    }
  3137.        SUB AX,BX       { := LINESIZE-bytes_per_plane2_row }
  3138.        MOV DX,4        {adjustment for source addresses of the tiles}
  3139.        SUB DX,BX       { := 4-bytes_per_plane2_row }
  3140.  
  3141.        PUSH SI         {save source- and destination address for other planes}
  3142.        PUSH DI
  3143.        PUSH BP         {save BP  }
  3144.        PUSH CX         {save row counter    }
  3145.        MOV BP,CX       {BP:=row counter  }
  3146.      @eineZeile3a:
  3147.        MOV CX,BX       {CX:=bytes_per_plane0_row }
  3148.        REP MOVSB       {move data of one row  }
  3149.        ADD DI,AX       {set DI to next destination row  }
  3150.        ADD SI,DX       {set SI to next tile's source addr.   }
  3151.        DEC BP
  3152.        JNZ @eineZeile3a
  3153.        POP CX
  3154.        POP BP
  3155.        POP DI
  3156.        POP SI
  3157.  
  3158.        MOV DX,3C4h
  3159.        MOV AX,0802h    {choose write plane 3  }
  3160.        OUT DX,AX
  3161.  
  3162.        MOV DX,3CEh     {next read plane:   }
  3163.        POP AX
  3164.        INC AH
  3165.        AND AH,3        {increment by 1 MOD 4}
  3166.        JNE @nowrap3a
  3167.        INC SI          {plane 0 follows after plane 3 again, but}
  3168.                        {the source address has increased by 1 byte }
  3169.      @nowrap3a:
  3170.        OUT DX,AX
  3171.  
  3172.  
  3173.        DEC BP          {BP:=16-leftcut}
  3174.      @lastplane1:
  3175.                      
  3176.        SHR BP,1        {use BP directly    }
  3177.        SHR BP,1        {BP:=bytes_per_plane3_row }
  3178.  
  3179.        MOV AX,LINESIZE {adjustment for row target addresses    }
  3180.        SUB AX,BP       { := LINESIZE-bytes_per_plane3_row }
  3181.        MOV DX,4        {adjustment for source addresses of the tiles}
  3182.        SUB DX,BP       { := 4-bytes_per_plane3_row }
  3183.  
  3184.                        {don't save source- and destination addresses}
  3185.        MOV BX,CX       {BX:=row counter  }
  3186.      @eineZeile4a:
  3187.        MOV CX,BP       {CX:=bytes_per_plane0_row }
  3188.        REP MOVSB       {move data of one row  }
  3189.        ADD DI,AX       {set DI to next destination row  }
  3190.        ADD SI,DX       {set SI to next tile's source addr.   }
  3191.        DEC BX
  3192.        JNZ @eineZeile4a
  3193.  
  3194.      @UpperLeftTileDone:
  3195.        POP BP
  3196.        MOV AX,SEG @Data  {restore DS         }
  3197.        MOV DS,AX
  3198.  
  3199.   {Now work on all other upper tiles, which are cut at their top (but }
  3200.   {not at the sides!): }
  3201.   @do_upperinnertiles:
  3202.     MOV AX,xpix
  3203.     MOV x,AX
  3204.     INC actIndex
  3205.  
  3206.   @repeat1:  {loop will be taken exactly "tiles" times   }
  3207.     MOV CL,offscreenFlag
  3208.     XOR SI,SI  {determine tile index:}
  3209.     DEC CL     {IF offscreenFlag OR (xt<0) OR (xt>=XTiles) }
  3210.     JZ @go2    { THEN index:=0 ELSE index:=actIndex =yt*XTiles+xt}
  3211.     MOV DX,xt
  3212.     OR DX,DX
  3213.     JS @go2
  3214.     CMP DX,XTiles
  3215.     JAE @go2
  3216.     MOV SI,actIndex
  3217.   @go2:
  3218.  
  3219.  
  3220.        {PROCEDURE DrawUpperTile(x,topcut:INTEGER; index:WORD);}
  3221.        { in: (x,0) = upper left corner of the tile to be drawn,}
  3222.        {     topcut= number of lines to be cut off}
  3223.        {     SI = index = tile number}
  3224.        {out: tile has been drawn on actual page PAGEADR       }
  3225.        {rem: Tile must be onscreen with its left, right (and lower) parts    }
  3226.        {     topcut must lie in stack (because DS is used for other purposes!)}
  3227.        {     topcut must have a value in the range 0..15, 16 is not allowed!}
  3228.        MOV AH,[OFFSET BackTile +SI]  {AH:=BackTile[index]}
  3229.        XOR AL,AL
  3230.  
  3231.        SHR AH,1    {AX:=tile*64 =tile SHL 6 =(tile SHL 8) SHR 2      }
  3232.        RCR AL,1    {therefore: set AH:=tile and afterwards, shift AX }
  3233.        SHR AH,1    {2 bits to the right!}
  3234.        RCR AL,1    {That's also the offset part of the source ad. in page3}
  3235.  
  3236.        MOV SI,topcut  {additionaly: the rows cut off at the upper edge:}
  3237.        MOV CX,16      {4 bytes for each row   }
  3238.        SUB CX,SI      {CX:=16-topcut = rows to draw        }
  3239.        SHL SI,1
  3240.        SHL SI,1
  3241.        ADD SI,AX   {SI = pointer to first tile byte to copy       }
  3242.  
  3243.        {The first destination address is DI:=0*LINESIZE+(x div 4)=x div 4}
  3244.        MOV DI,x
  3245.        MOV BX,DI   {have a copy of X in BX       }
  3246.        SHR DI,1
  3247.        SHR DI,1
  3248.  
  3249.        {Now there is no further variable on stack, so BP can be used   }
  3250.        {for other purposes!}
  3251.        PUSH BP     {will be needed when leaving the procedure! }
  3252.        MOV BP,CX
  3253.        SHL BP,1
  3254.        SHL BP,1    {BP := (rows to draw)*4         }
  3255.  
  3256.        MOV ES,PAGEADR    {(segment part of) dest. address is active page}
  3257.        MOV DS,SCROLLADR  {(segment part of) source addr. is page SCROLLPAGE}
  3258.  
  3259.        MOV DX,3C4h
  3260.        MOV AL,2
  3261.        AND BX,3    {BX:=1st write plane (1st READ plane=0!)   }
  3262.        JNE @mode0b {only if x mod 4=0 is it possible to use WriteMode1      }
  3263.  
  3264.        {--- short-cut possible, using write mode1   }
  3265.        MOV AH,0Fh  {work on all 4 planes simultaneously  }
  3266.        OUT DX,AX
  3267.        MOV AX,4105h
  3268.        MOV DX,3CEh
  3269.        OUT DX,AX   {choose write mode 1}
  3270.        MOV BX,CX   {BX:=CX=rows to draw        }
  3271.  
  3272.        MOV AX,LINESIZE-4
  3273.        MOV CX,BX   {CX:=number of rows to draw  }
  3274.      @eineZeile4b1:
  3275.        MOVSB       {no MOVSW, as we use write mode 1!}
  3276.        MOVSB
  3277.        MOVSB
  3278.        MOVSB
  3279.        ADD DI,AX   {set DI to next row          }
  3280.        LOOP @eineZeile4b1
  3281.  
  3282.        {--- short cut has been taken: thus, reset write mode 0!     }
  3283.        MOV AX,4005h
  3284.        MOV DX,3CEh
  3285.        OUT DX,AX
  3286.        {---}
  3287.        JMP @UpperTileDone
  3288.  
  3289.      @mode0b:
  3290.        MOV AH,CS:[OFFSET CS_TranslateTab+BX]
  3291.        OUT DX,AX
  3292.        PUSH AX     {save actual write plane     }
  3293.  
  3294.        MOV DX,3CEh
  3295.        MOV AX,0004h    {choose plane 0 for reading  }
  3296.        OUT DX,AX
  3297.  
  3298.        MOV AX,LINESIZE-4 {adjustment for row addresses       }
  3299.        MOV BX,CX   {save number of rows to draw into BX    }
  3300.      @eineZeile1b:
  3301.        MOVSW
  3302.        MOVSW
  3303.        ADD DI,AX   {set DI to next row          }
  3304.        LOOP @eineZeile1b
  3305.  
  3306.        MOV AX,0104h  {DX=3CEh -> choose read plane 1}
  3307.        OUT DX,AX
  3308.  
  3309.        MOV DX,3C4h   {next write plane:     }
  3310.        POP AX
  3311.        SHL AH,1
  3312.        CMP AH,16
  3313.        JNE @nowrap1b
  3314.        MOV AH,1      {plane 0 follows after plane 3 again, but}
  3315.        INC DI        {the destination address has increased by 1 byte}
  3316.      @nowrap1b:
  3317.        OUT DX,AX
  3318.        PUSH AX
  3319.        SHL BX,1      {reset DI to the source address:              }
  3320.        SUB DI,CS:[OFFSET GADR + BX] {thus, decrement DI by (rows to draw)*     }
  3321.        SHR BX,1      {LINESIZE (N.B.: BX=number of rows to draw)}
  3322.        SUB SI,BP     {reset SI, too        }
  3323.  
  3324.  
  3325.        MOV AX,LINESIZE-4
  3326.        MOV CX,BX   {CX:=number of rows to draw    }
  3327.      @eineZeile2b:
  3328.        MOVSW
  3329.        MOVSW
  3330.        ADD DI,AX   {set DI to next row          }
  3331.        LOOP @eineZeile2b
  3332.  
  3333.        POP AX
  3334.        SHL AH,1
  3335.        CMP AH,16
  3336.        JNE @nowrap2b
  3337.        MOV AH,1      {plane 0 follows after plane 3 again, but}
  3338.        INC DI        {the destination address has increased by 1 byte}
  3339.      @nowrap2b:
  3340.        OUT DX,AX     {DX=3C4h -> set write plane    }
  3341.        PUSH AX
  3342.        MOV DX,3CEh
  3343.        MOV AX,0204h  {set read plane 2  }
  3344.        OUT DX,AX
  3345.        SHL BX,1      {reset DI to the source address:              }
  3346.        SUB DI,CS:[OFFSET GADR + BX] {thus, decrement DI by (rows to draw)*     }
  3347.        SHR BX,1      {LINESIZE (N.B.: BX=number of rows to draw)}
  3348.        SUB SI,BP     {reset SI, too        }
  3349.  
  3350.  
  3351.        MOV AX,LINESIZE-4
  3352.        MOV CX,BX   {CX:=number of rows to draw  }
  3353.      @eineZeile3b:
  3354.        MOVSW
  3355.        MOVSW
  3356.        ADD DI,AX   {set DI to next row          }
  3357.        LOOP @eineZeile3b
  3358.  
  3359.        MOV AX,0304h
  3360.        OUT DX,AX     {DX=3CEh -> choose read plane 3}
  3361.        MOV DX,3C4h
  3362.        POP AX
  3363.        SHL AH,1
  3364.        CMP AH,16
  3365.        JNE @nowrap3b
  3366.        MOV AH,1      {plane 0 follows after plane 3 again, but}
  3367.        INC DI        {the destination address has increased by 1 byte}
  3368.      @nowrap3b:
  3369.        OUT DX,AX     {DX=3C4h -> set write plane    }
  3370.        SHL BX,1      {reset DI to the source address:              }
  3371.        SUB DI,CS:[OFFSET GADR + BX] {thus, decrement DI by (rows to draw)*     }
  3372.        SHR BX,1      {LINESIZE (N.B.: BX=number of rows to draw)}
  3373.        SUB SI,BP     {reset SI, too        }
  3374.  
  3375.  
  3376.      @lastPlane2:
  3377.        MOV AX,LINESIZE-4
  3378.        MOV CX,BX   {CX:=number of rows to draw  }
  3379.      @eineZeile4b:
  3380.        MOVSW
  3381.        MOVSW
  3382.        ADD DI,AX   {set DI to next row          }
  3383.        LOOP @eineZeile4b
  3384.  
  3385.      @UpperTileDone:
  3386.        POP BP
  3387.        MOV AX,SEG @Data  {restore DS         }
  3388.        MOV DS,AX
  3389.  
  3390.     INC xt        {increment tile counter in X-direction}
  3391.     INC actIndex  {because actIndex=yt*XTiles+xt do increment actIndex, too}
  3392.     MOV AX,x      {increment actual X-coordinate, too: x:=x+16 }
  3393.     ADD AX,16
  3394.     MOV x,AX
  3395.     CMP AX,XMAX+1-16
  3396.     JBE @repeat1  {until upper right corner has been reached}
  3397.  
  3398.     {Now the upper right corner-tile - if not already drawn:      }
  3399.     CMP AX,XMAX+1
  3400.     JE @label1
  3401.  
  3402.     MOV CL,offscreenFlag  {yes, corner remains to be drawn     }
  3403.     XOR SI,SI  {determine tile index:}
  3404.     DEC CL     {IF offscreenFlag OR (xt<0) OR (xt>=XTiles) }
  3405.     JZ @go3    { THEN index:=0 ELSE index:=actIndex =yt*XTiles+xt}
  3406.     MOV DX,xt
  3407.     OR DX,DX
  3408.     JS @go3
  3409.     CMP DX,XTiles
  3410.     JAE @go3
  3411.     MOV SI,actIndex
  3412.   @go3:
  3413.  
  3414.  
  3415.        {PROCEDURE DrawUpperRightTile(x,rightcut,topcut:INTEGER; index:WORD);}
  3416.        { in: (x,0) = upper left corner of the tile to be drawn}
  3417.        {     rightcut = number of right tile-columns to be cut off     }
  3418.        {     topcut   = dto., at the top}
  3419.        {     SI = index = tile number}
  3420.        {out: tile has been drawn on actual page PAGEADR       }
  3421.        {rem: tile has been clipped at its right and top accordingly}
  3422.        {     topcut must lie in the range 0..15, 16 is not allowed}
  3423.        {     rightcut must lie between 0..15, 16 is allowed (but senseless)!        }
  3424.        {     rightcut could also be computed (for x>xmax-16) by             }
  3425.        {     rightcut := x+15-xmax }
  3426.        MOV AH,[OFFSET BackTile +SI]  {AH:=BackTile[index]}
  3427.        XOR AL,AL
  3428.  
  3429.        SHR AH,1    {AX:=tile*64 =tile SHL 6 =(tile SHL 8) SHR 2      }
  3430.        RCR AL,1    {therefore: set AH:=tile and afterwards, shift AX }
  3431.        SHR AH,1    {2 bits to the right!}
  3432.        RCR AL,1
  3433.        MOV SI,topcut
  3434.        MOV CX,16
  3435.        SUB CX,SI   {CX:=16-topcut = number of rows to draw     }
  3436.        SHL SI,1
  3437.        SHL SI,1    {add 4 bytes to the (offset part of the)    }
  3438.        ADD SI,AX   {source addr. in page 3 for each cut off top-row}
  3439.  
  3440.        MOV DI,x    {first dest. addr. is DI:=0*LINESIZE +(x div 4)=x div 4 }
  3441.        MOV BX,DI   {place a copy of x in BX}
  3442.        SHR DI,1
  3443.        SHR DI,1
  3444.  
  3445.        MOV AX,rightcut
  3446.  
  3447.        {Now there is no further variable on stack, so BP can be used   }
  3448.        {for other purposes!}
  3449.        PUSH BP     {will be needed when leaving the procedure! }
  3450.        MOV BP,16+3
  3451.        SUB BP,AX   {BP:=16+3-rightcut, because the number of bytes per row  }
  3452.                    {of plane i is computed by (16+3-i-rightcut) SHR 2 }
  3453.        MOV AH,AL   {save rightcut to AH    }
  3454.  
  3455.        MOV ES,PAGEADR    {(segment part of) dest. address is active page}
  3456.        MOV DS,SCROLLADR  {(segment part of) source addr. is page SCROLLPAGE}
  3457.  
  3458.        MOV DX,3C4h
  3459.        MOV AL,2
  3460.        AND BX,3
  3461.  
  3462.        JNE @mode0c {only if x mod 4=0 _and_ rightcut mod 4=0, then we can }
  3463.        AND AH,3    {use write mode 1!        }
  3464.        JNE @mode0c
  3465.  
  3466.        {--- short-cut possible, using write mode1: DX=3C4h, BP=16+3-rightcut   }
  3467.        MOV AH,0Fh
  3468.        OUT DX,AX    {work on all 4 planes simultaneously  }
  3469.        MOV DX,3CEh
  3470.        MOV AX,4105h {choose write mode 1}
  3471.        OUT DX,AX
  3472.        SUB BP,3     {set BP to proper size              }
  3473.  
  3474.        SHR BP,1      {use BP directly    }
  3475.        SHR BP,1      {BP:=bytes_per_plane3_row }
  3476.  
  3477.        MOV AX,LINESIZE {adjustment for row target addresses    }
  3478.        SUB AX,BP       { := LINESIZE-bytes_per_plane3_row }
  3479.        MOV DX,4        {adjustment for source addresses of the tiles}
  3480.        SUB DX,BP       { := 4-bytes_per_plane3_row }
  3481.  
  3482.                        {don't save source- and destination addresses}
  3483.        MOV BX,CX   {BX:=row counter  }
  3484.      @eineZeile4c1:
  3485.        MOV CX,BP   {CX:=bytes_per_plane0_row }
  3486.        REP MOVSB   {move data of one row  }
  3487.        ADD DI,AX   {set DI to next destination row  }
  3488.        ADD SI,DX   {set SI to next tile's source addr.   }
  3489.        DEC BX
  3490.        JNZ @eineZeile4c1
  3491.  
  3492.        {--- short cut has been taken: thus, reset write mode 0!     }
  3493.        MOV AX,4005h
  3494.        MOV DX,3CEh
  3495.        OUT DX,AX
  3496.        {---}
  3497.        JMP @UpperRightDone
  3498.  
  3499.      @mode0c:
  3500.        MOV AH,CS:[OFFSET CS_TranslateTab +BX]
  3501.        OUT DX,AX   {choose plane to write at     }
  3502.        PUSH AX     {and preserve it for later}
  3503.  
  3504.        MOV DX,3CEh
  3505.        MOV AX,0004h    {choose read plane 0}
  3506.        OUT DX,AX
  3507.  
  3508.        MOV BX,BP
  3509.        SHR BX,1
  3510.        SHR BX,1    {BX:=bytes_per_plane0_row = (16+3-rightcut) DIV 4 }
  3511.  
  3512.        MOV AX,LINESIZE {adjustment for row target addresses    }
  3513.        SUB AX,BX       { := LINESIZE-bytes_per_plane0_row }
  3514.        MOV DX,4        {adjustment for source addresses of the tiles}
  3515.        SUB DX,BX       { := 4-bytes_per_plane0_row }
  3516.  
  3517.        PUSH SI     {save source- and destination address for other planes}
  3518.        PUSH DI
  3519.        PUSH BP     {save BP  }
  3520.        PUSH CX     {CX = save row counter    }
  3521.        MOV BP,CX   {BP:=row counter  }
  3522.      @eineZeile1c:
  3523.        MOV CX,BX   {CX:=bytes_per_plane0_row }
  3524.        REP MOVSB   {move data of one row  }
  3525.        ADD DI,AX   {set DI to next destination row  }
  3526.        ADD SI,DX   {set SI to next tile's source addr.   }
  3527.        DEC BP
  3528.        JNZ @eineZeile1c
  3529.        POP CX
  3530.        POP BP
  3531.        POP DI
  3532.        POP SI
  3533.  
  3534.        MOV DX,3CEh
  3535.        MOV AX,0104h  {choose read plane 1}
  3536.        OUT DX,AX
  3537.  
  3538.        MOV DX,3C4h   {next write plane:     }
  3539.        POP AX
  3540.        SHL AH,1
  3541.        CMP AH,16
  3542.        JNE @nowrap1c
  3543.        MOV AH,1      {plane 0 follows after plane 3 again, but}
  3544.        INC DI        {the destination address has increased by 1 byte}
  3545.      @nowrap1c:
  3546.        OUT DX,AX
  3547.        PUSH AX
  3548.  
  3549.  
  3550.        DEC BP        {BP:=16+2-rightcut}
  3551.        MOV BX,BP
  3552.        SHR BX,1
  3553.        SHR BX,1      {BX:=bytes_per_plane1_row }
  3554.  
  3555.        MOV AX,LINESIZE {adjustment for row target addresses    }
  3556.        SUB AX,BX       { := LINESIZE-bytes_per_plane1_row }
  3557.        MOV DX,4        {adjustment for source addresses of the tiles}
  3558.        SUB DX,BX       { := 4-bytes_per_plane1_row }
  3559.  
  3560.        PUSH SI     {save source- and destination address for other planes}
  3561.        PUSH DI
  3562.        PUSH BP     {save BP  }
  3563.        PUSH CX     {CX = save row counter    }
  3564.        MOV BP,CX   {BP:=row counter  }
  3565.      @eineZeile2c:
  3566.        MOV CX,BX   {CX:=bytes_per_plane0_row }
  3567.        REP MOVSB   {move data of one row  }
  3568.        ADD DI,AX   {set DI to next destination row  }
  3569.        ADD SI,DX   {set SI to next tile's source addr.   }
  3570.        DEC BP
  3571.        JNZ @eineZeile2c
  3572.        POP CX
  3573.        POP BP
  3574.        POP DI
  3575.        POP SI
  3576.  
  3577.        MOV DX,3CEh
  3578.        MOV AX,0204h  {choose read plane 2}
  3579.        OUT DX,AX
  3580.  
  3581.        MOV DX,3C4h   {next write plane:     }
  3582.        POP AX
  3583.        SHL AH,1
  3584.        CMP AH,16
  3585.        JNE @nowrap2c
  3586.        MOV AH,1      {plane 0 follows after plane 3 again, but}
  3587.        INC DI        {the destination address has increased by 1 byte}
  3588.      @nowrap2c:
  3589.        OUT DX,AX
  3590.        PUSH AX
  3591.  
  3592.  
  3593.        DEC BP        {BP:=16+1-rightcut}
  3594.        MOV BX,BP
  3595.        SHR BX,1
  3596.        SHR BX,1      {BX:=bytes_per_plane2_row }
  3597.  
  3598.        MOV AX,LINESIZE {adjustment for row target addresses    }
  3599.        SUB AX,BX       { := LINESIZE-bytes_per_plane2_row }
  3600.        MOV DX,4        {adjustment for source addresses of the tiles}
  3601.        SUB DX,BX       { := 4-bytes_per_plane2_row }
  3602.  
  3603.        PUSH SI     {save source- and destination address for other planes}
  3604.        PUSH DI
  3605.        PUSH BP     {save BP  }
  3606.        PUSH CX     {CX = save row counter    }
  3607.        MOV BP,CX   {BP:=row counter  }
  3608.      @eineZeile3c:
  3609.        MOV CX,BX   {CX:=bytes_per_plane0_row }
  3610.        REP MOVSB   {move data of one row  }
  3611.        ADD DI,AX   {set DI to next destination row  }
  3612.        ADD SI,DX   {set SI to next tile's source addr.   }
  3613.        DEC BP
  3614.        JNZ @eineZeile3c
  3615.        POP CX
  3616.        POP BP
  3617.        POP DI
  3618.        POP SI
  3619.  
  3620.        MOV DX,3CEh
  3621.        MOV AX,0304h  {choose read plane 3}
  3622.        OUT DX,AX
  3623.  
  3624.        MOV DX,3C4h   {next write plane:     }
  3625.        POP AX
  3626.        SHL AH,1
  3627.        CMP AH,16
  3628.        JNE @nowrap3c
  3629.        MOV AH,1      {plane 0 follows after plane 3 again, but}
  3630.        INC DI        {the destination address has increased by 1 byte}
  3631.      @nowrap3c:
  3632.        OUT DX,AX
  3633.                      {do not push value again!}
  3634.  
  3635.  
  3636.        DEC BP        {BP:=16-rightcut}
  3637.      @lastplane3:
  3638.                      
  3639.        SHR BP,1      {use BP directly    }
  3640.        SHR BP,1      {BP:=bytes_per_plane3_row }
  3641.  
  3642.        MOV AX,LINESIZE {adjustment for row target addresses    }
  3643.        SUB AX,BP       { := LINESIZE-bytes_per_plane3_row }
  3644.        MOV DX,4        {adjustment for source addresses of the tiles}
  3645.        SUB DX,BP       { := 4-bytes_per_plane3_row }
  3646.  
  3647.                        {don't save source- and destination addresses}
  3648.        MOV BX,CX   {BX:=row counter  }
  3649.      @eineZeile4c:
  3650.        MOV CX,BP   {CX:=bytes_per_plane0_row }
  3651.        REP MOVSB   {move data of one row  }
  3652.        ADD DI,AX   {set DI to next destination row  }
  3653.        ADD SI,DX   {set SI to next tile's source addr.   }
  3654.        DEC BX
  3655.        JNZ @eineZeile4c
  3656.  
  3657.      @UpperRightDone:
  3658.        POP BP
  3659.        MOV AX,SEG @Data  {restore DS         }
  3660.        MOV DS,AX
  3661.  
  3662.   @label1:
  3663.     MOV AX,actIndex  {adjust actIndex for next tile-row:         }
  3664.     STC              {actIndex:=actIndex-tiles+1}
  3665.     SBB AX,tiles
  3666.     MOV actIndex,AX
  3667.  
  3668.   {Now draw all tiles which are completely onscreen             }
  3669.   {(that is: all non-cut) tiles:               }
  3670.   @do_innertiles:
  3671.     MOV AX,ypix
  3672.     MOV y,AX
  3673.     MOV AX,actIndex
  3674.     ADD AX,XTiles
  3675.     MOV RandIndex,AX
  3676.     INC AX
  3677.     MOV actIndex,AX
  3678.  
  3679.   @repeat2:
  3680.     MOV AX,ytil
  3681.     MOV CL,1
  3682.     OR AX,AX
  3683.     JS @go4
  3684.     CMP AX,YTiles
  3685.     JAE @go4
  3686.     DEC CL
  3687.   @go4:
  3688.     MOV offscreenFlag,CL
  3689.     MOV AX,xpix
  3690.     MOV x,AX
  3691.     MOV AX,offsetXTiles
  3692.     MOV xtil,AX
  3693.  
  3694.   @repeat3:
  3695.     MOV CL,offscreenFlag
  3696.     XOR SI,SI
  3697.     DEC CL
  3698.     JZ @go5
  3699.     OR AX,AX
  3700.     JS @go5
  3701.     CMP AX,XTiles
  3702.     JAE @go5
  3703.     MOV SI,actIndex
  3704.   @go5:
  3705.  
  3706.  
  3707.        {PROCEDURE DrawInnerTile(x,y:INTEGER; index:WORD);}
  3708.        { in: (x,y) = upper left corner of the tile to be drawn,}
  3709.        {     SI = index = tile number}
  3710.        {out: tile has been drawn on actual page PAGEADR       }
  3711.        {rem: tile must be completely onscreen, there will be  }
  3712.        {     no check for that  }
  3713.        MOV AH,[OFFSET BackTile +SI]  {AH:=BackTile[index]}
  3714.        XOR AL,AL
  3715.  
  3716.        SHR AH,1    {AX:=tile*64 =tile SHL 6 =(tile SHL 8) SHR 2      }
  3717.        RCR AL,1    {therefore: set AH:=tile and afterwards, shift AX }
  3718.        SHR AH,1    {2 bits to the right!}
  3719.        RCR AL,1
  3720.        MOV SI,AX   {That's also the offset part of the source ad. in page3}
  3721.  
  3722.        MOV DI,y    {the first destination addr. is DI:=y*LINESIZE+(x div 4)}
  3723.        SHL DI,1
  3724.        MOV DI,CS:[OFFSET GADR + DI]
  3725.        MOV AX,x
  3726.        MOV BX,AX   {have a copy of X in BX       }
  3727.        SHR AX,1
  3728.        SHR AX,1
  3729.        ADD DI,AX
  3730.  
  3731.        MOV ES,PAGEADR    {(segment part of) dest. address is active page}
  3732.        MOV DS,SCROLLADR  {(segment part of) source addr. is page SCROLLPAGE}
  3733.  
  3734.        MOV DX,3C4h
  3735.        MOV AL,2
  3736.        AND BX,3    {BX:=1st write plane (1st READ plane=0!)   }
  3737.        JNE @mode0d {only if x mod 4=0 is it possible to use WriteMode1      }
  3738.  
  3739.        {--- short-cut possible, using write mode1   }
  3740.        MOV AH,0Fh  {work on all 4 planes simultaneously  }
  3741.        OUT DX,AX
  3742.        MOV AX,4105h
  3743.        MOV DX,3CEh
  3744.        OUT DX,AX   {choose write mode 1}
  3745.        MOV BX,4
  3746.  
  3747.        MOV AX,LINESIZE-4
  3748.        MOV BX,4    {16 pixels = 4 bytes}
  3749.        MOV CX,BX   {no "MOVSW", as we use write mode1}
  3750.        REP MOVSB   {draw 1st row    }
  3751.        ADD DI,AX   {set DI to next row          }
  3752.        MOV CX,BX
  3753.        REP MOVSB   {2.row  }
  3754.        ADD DI,AX
  3755.        MOV CX,BX
  3756.        REP MOVSB   {3.row  }
  3757.        ADD DI,AX
  3758.        MOV CX,BX
  3759.        REP MOVSB   {4.row  }
  3760.        ADD DI,AX
  3761.        MOV CX,BX
  3762.        REP MOVSB   {5.row  }
  3763.        ADD DI,AX
  3764.        MOV CX,BX
  3765.        REP MOVSB   {6.row  }
  3766.        ADD DI,AX
  3767.        MOV CX,BX
  3768.        REP MOVSB   {7.row  }
  3769.        ADD DI,AX
  3770.        MOV CX,BX
  3771.        REP MOVSB   {8.row  }
  3772.        ADD DI,AX
  3773.        MOV CX,BX
  3774.        REP MOVSB   {9.row  }
  3775.        ADD DI,AX
  3776.        MOV CX,BX
  3777.        REP MOVSB   {10.row  }
  3778.        ADD DI,AX
  3779.        MOV CX,BX
  3780.        REP MOVSB   {11.row  }
  3781.        ADD DI,AX
  3782.        MOV CX,BX
  3783.        REP MOVSB   {12.row  }
  3784.        ADD DI,AX
  3785.        MOV CX,BX
  3786.        REP MOVSB   {13.row  }
  3787.        ADD DI,AX
  3788.        MOV CX,BX
  3789.        REP MOVSB   {14.row  }
  3790.        ADD DI,AX
  3791.        MOV CX,BX
  3792.        REP MOVSB   {15.row  }
  3793.        ADD DI,AX
  3794.        MOV CX,BX
  3795.        REP MOVSB   {16.row  }
  3796.  
  3797.  
  3798.        {--- short cut has been taken: thus, reset write mode 0!     }
  3799.        MOV AX,4005h
  3800.        MOV DX,3CEh
  3801.        OUT DX,AX
  3802.        {---}
  3803.        JMP @InnerTileDone
  3804.  
  3805.      @mode0d:
  3806.        MOV AH,CS:[OFFSET CS_TranslateTab+BX]
  3807.        OUT DX,AX
  3808.        PUSH AX     {save actual write plane     }
  3809.  
  3810.        MOV DX,3CEh
  3811.        MOV AX,0004h    {choose plane 0 for reading  }
  3812.        OUT DX,AX
  3813.  
  3814.        MOV AX,LINESIZE-4 {adjustment for row addresses       }
  3815.  
  3816.        MOVSW       {16 horizontal pixels = 4 bytes per line  }
  3817.        MOVSW       {draw 1st row    }
  3818.        ADD DI,AX   {set DI to next row          }
  3819.        MOVSW       {2.row  }
  3820.        MOVSW
  3821.        ADD DI,AX
  3822.        MOVSW
  3823.        MOVSW       {3.row  }
  3824.        ADD DI,AX
  3825.        MOVSW
  3826.        MOVSW       {4.row  }
  3827.        ADD DI,AX
  3828.        MOVSW
  3829.        MOVSW       {5.row  }
  3830.        ADD DI,AX
  3831.        MOVSW
  3832.        MOVSW       {6.row  }
  3833.        ADD DI,AX
  3834.        MOVSW
  3835.        MOVSW       {7.row  }
  3836.        ADD DI,AX
  3837.        MOVSW
  3838.        MOVSW       {8.row  }
  3839.        ADD DI,AX
  3840.        MOVSW
  3841.        MOVSW       {9.row  }
  3842.        ADD DI,AX
  3843.        MOVSW
  3844.        MOVSW      {10.row  }
  3845.        ADD DI,AX
  3846.        MOVSW
  3847.        MOVSW      {11.row  }
  3848.        ADD DI,AX
  3849.        MOVSW
  3850.        MOVSW      {12.row  }
  3851.        ADD DI,AX
  3852.        MOVSW
  3853.        MOVSW      {13.row  }
  3854.        ADD DI,AX
  3855.        MOVSW
  3856.        MOVSW      {14.row  }
  3857.        ADD DI,AX
  3858.        MOVSW
  3859.        MOVSW      {15.row  }
  3860.        ADD DI,AX
  3861.        MOVSW
  3862.        MOVSW      {16.row  }
  3863.        ADD DI,AX
  3864.  
  3865.        MOV AX,0104h  {DX=3CEh -> choose read plane 1}
  3866.        OUT DX,AX
  3867.  
  3868.        MOV DX,3C4h   {next write plane:     }
  3869.        POP AX
  3870.        SHL AH,1
  3871.        CMP AH,16
  3872.        JNE @nowrap1d
  3873.        MOV AH,1      {plane 0 follows after plane 3 again, but}
  3874.        INC DI        {the destination address has increased by 1 byte}
  3875.      @nowrap1d:
  3876.        OUT DX,AX
  3877.        PUSH AX
  3878.        SUB DI,16*LINESIZE   {reset DI to the source address              }
  3879.        SUB SI,16*4          {SI, too}
  3880.  
  3881.  
  3882.        MOV AX,LINESIZE-4
  3883.        MOVSW
  3884.        MOVSW       {draw 1st row    }
  3885.        ADD DI,AX   {set DI to next row          }
  3886.        MOVSW
  3887.        MOVSW       {2.row  }
  3888.        ADD DI,AX
  3889.        MOVSW
  3890.        MOVSW       {3.row  }
  3891.        ADD DI,AX
  3892.        MOVSW
  3893.        MOVSW       {4.row  }
  3894.        ADD DI,AX
  3895.        MOVSW
  3896.        MOVSW       {5.row  }
  3897.        ADD DI,AX
  3898.        MOVSW
  3899.        MOVSW       {6.row  }
  3900.        ADD DI,AX
  3901.        MOVSW
  3902.        MOVSW       {7.row  }
  3903.        ADD DI,AX
  3904.        MOVSW
  3905.        MOVSW       {8.row  }
  3906.        ADD DI,AX
  3907.        MOVSW
  3908.        MOVSW       {9.row  }
  3909.        ADD DI,AX
  3910.        MOVSW
  3911.        MOVSW      {10.row  }
  3912.        ADD DI,AX
  3913.        MOVSW
  3914.        MOVSW      {11.row  }
  3915.        ADD DI,AX
  3916.        MOVSW
  3917.        MOVSW      {12.row  }
  3918.        ADD DI,AX
  3919.        MOVSW
  3920.        MOVSW      {13.row  }
  3921.        ADD DI,AX
  3922.        MOVSW
  3923.        MOVSW      {14.row  }
  3924.        ADD DI,AX
  3925.        MOVSW
  3926.        MOVSW      {15.row  }
  3927.        ADD DI,AX
  3928.        MOVSW
  3929.        MOVSW      {16.row  }
  3930.        ADD DI,AX
  3931.  
  3932.        POP AX
  3933.        SHL AH,1
  3934.        CMP AH,16
  3935.        JNE @nowrap2d
  3936.        MOV AH,1      {plane 0 follows after plane 3 again, but}
  3937.        INC DI        {the destination address has increased by 1 byte}
  3938.      @nowrap2d:
  3939.        OUT DX,AX     {DX=3C4h -> set write plane    }
  3940.        PUSH AX
  3941.        MOV DX,3CEh
  3942.        MOV AX,0204h  {set read plane 2  }
  3943.        OUT DX,AX
  3944.        SUB DI,16*LINESIZE
  3945.        SUB SI,16*4
  3946.  
  3947.  
  3948.        MOV AX,LINESIZE-4
  3949.        MOVSW
  3950.        MOVSW       {draw 1st row    }
  3951.        ADD DI,AX   {set DI to next row          }
  3952.        MOVSW
  3953.        MOVSW       {2.row  }
  3954.        ADD DI,AX
  3955.        MOVSW
  3956.        MOVSW       {3.row  }
  3957.        ADD DI,AX
  3958.        MOVSW
  3959.        MOVSW       {4.row  }
  3960.        ADD DI,AX
  3961.        MOVSW
  3962.        MOVSW       {5.row  }
  3963.        ADD DI,AX
  3964.        MOVSW
  3965.        MOVSW       {6.row  }
  3966.        ADD DI,AX
  3967.        MOVSW
  3968.        MOVSW       {7.row  }
  3969.        ADD DI,AX
  3970.        MOVSW
  3971.        MOVSW       {8.row  }
  3972.        ADD DI,AX
  3973.        MOVSW
  3974.        MOVSW       {9.row  }
  3975.        ADD DI,AX
  3976.        MOVSW
  3977.        MOVSW      {10.row  }
  3978.        ADD DI,AX
  3979.        MOVSW
  3980.        MOVSW      {11.row  }
  3981.        ADD DI,AX
  3982.        MOVSW
  3983.        MOVSW      {12.row  }
  3984.        ADD DI,AX
  3985.        MOVSW
  3986.        MOVSW      {13.row  }
  3987.        ADD DI,AX
  3988.        MOVSW
  3989.        MOVSW      {14.row  }
  3990.        ADD DI,AX
  3991.        MOVSW
  3992.        MOVSW      {15.row  }
  3993.        ADD DI,AX
  3994.        MOVSW
  3995.        MOVSW      {16.row  }
  3996.        ADD DI,AX
  3997.  
  3998.        MOV AX,0304h
  3999.        OUT DX,AX     {DX=3CEh -> choose read plane 3}
  4000.        MOV DX,3C4h
  4001.        POP AX
  4002.        SHL AH,1
  4003.        CMP AH,16
  4004.        JNE @nowrap3d
  4005.        MOV AH,1      {plane 0 follows after plane 3 again, but}
  4006.        INC DI        {the destination address has increased by 1 byte}
  4007.      @nowrap3d:
  4008.        OUT DX,AX     {DX=3C4h -> set write plane    }
  4009.        SUB DI,16*LINESIZE
  4010.        SUB SI,16*4
  4011.  
  4012.  
  4013.      @lastPlane4:
  4014.        MOV AX,LINESIZE-4
  4015.        MOVSW
  4016.        MOVSW       {draw 1st row    }
  4017.        ADD DI,AX   {set DI to next row          }
  4018.        MOVSW
  4019.        MOVSW       {2.row  }
  4020.        ADD DI,AX
  4021.        MOVSW
  4022.        MOVSW       {3.row  }
  4023.        ADD DI,AX
  4024.        MOVSW
  4025.        MOVSW       {4.row  }
  4026.        ADD DI,AX
  4027.        MOVSW
  4028.        MOVSW       {5.row  }
  4029.        ADD DI,AX
  4030.        MOVSW
  4031.        MOVSW       {6.row  }
  4032.        ADD DI,AX
  4033.        MOVSW
  4034.        MOVSW       {7.row  }
  4035.        ADD DI,AX
  4036.        MOVSW
  4037.        MOVSW       {8.row  }
  4038.        ADD DI,AX
  4039.        MOVSW
  4040.        MOVSW       {9.row  }
  4041.        ADD DI,AX
  4042.        MOVSW
  4043.        MOVSW      {10.row  }
  4044.        ADD DI,AX
  4045.        MOVSW
  4046.        MOVSW      {11.row  }
  4047.        ADD DI,AX
  4048.        MOVSW
  4049.        MOVSW      {12.row  }
  4050.        ADD DI,AX
  4051.        MOVSW
  4052.        MOVSW      {13.row  }
  4053.        ADD DI,AX
  4054.        MOVSW
  4055.        MOVSW      {14.row  }
  4056.        ADD DI,AX
  4057.        MOVSW
  4058.        MOVSW      {15.row  }
  4059.        ADD DI,AX
  4060.        MOVSW
  4061.        MOVSW      {16.row  }
  4062.  
  4063.  
  4064.      @InnerTileDone:
  4065.        MOV AX,SEG @Data  {restore DS         }
  4066.        MOV DS,AX
  4067.  
  4068.     INC actIndex
  4069.     MOV AX,xtil
  4070.     INC AX
  4071.     MOV xtil,AX
  4072.     MOV DX,x
  4073.     ADD DX,16
  4074.     MOV x,DX
  4075.     CMP DX,XMAX+1-16
  4076.     JBE @repeat3
  4077.  
  4078.     INC ytil
  4079.     MOV AX,actIndex
  4080.     SUB AX,tiles
  4081.     ADD AX,XTiles
  4082.     MOV actIndex,AX
  4083.     MOV AX,y
  4084.     ADD AX,16
  4085.     MOV y,AX
  4086.     CMP AX,YMAX+1-16
  4087.     JBE @repeat2
  4088.  
  4089.     {Now draw the lower right tile - if it doesn't have been drawn by one of}
  4090.     {routines above: }
  4091.     CMP bottomcut,0
  4092.     JE @do_raender
  4093.     MOV DX,offsetXTiles
  4094.     MOV xt,DX
  4095.     MOV AX,ytil
  4096.     MOV yt,AX
  4097.     MOV CL,1
  4098.     OR AX,AX
  4099.     JS @go6
  4100.     CMP AX,YTiles
  4101.     JAE @go6
  4102.     DEC CL
  4103.   @go6:
  4104.     MOV offscreenFlag,CL
  4105.     CMP leftcut,0
  4106.     JE @label2
  4107.     XOR SI,SI
  4108.     DEC CL
  4109.     JZ @go7
  4110.     DEC DX  {DX=xt-1}
  4111.     JS @go7
  4112.     CMP DX,XTiles
  4113.     JAE @go7
  4114.     MOV SI,actIndex
  4115.     DEC SI
  4116.   @go7:
  4117.  
  4118.        {PROCEDURE DrawLowerLeftTile(y,leftcut,bottomcut:INTEGER; index:WORD);}
  4119.        { in: (0,y) = upper left corner of the tile to be drawn}
  4120.        {     leftcut = number of left tile-columns to be cut off     }
  4121.        {     bottomcut = dto., bottom}
  4122.        {     SI = index = tile number}
  4123.        {out: tile has been drawn on actual page PAGEADR       }
  4124.        {rem: tile has been clipped at its left and bottom accordingly}
  4125.        {     leftcut must lie in the range 0..15, 16 is allowed (but senseless)   }
  4126.        {     bottomcut must lie in the range 0..15, 16 is not allowed}
  4127.        {     bottomcut could also be computed by:             }
  4128.        {     bottomcut:=y+15-ymax}
  4129.        MOV AH,[OFFSET BackTile +SI]  {AH:=BackTile[index]}
  4130.        XOR AL,AL
  4131.  
  4132.        SHR AH,1    {AX:=tile*64 =tile SHL 6 =(tile SHL 8) SHR 2      }
  4133.        RCR AL,1    {therefore: set AH:=tile and afterwards, shift AX }
  4134.        SHR AH,1    {2 bits to the right!}
  4135.        RCR AL,1
  4136.        MOV SI,AX   {That's also the offset part of the source ad. in page3}
  4137.  
  4138.        MOV DI,y    {first dest. addr. is DI:=y*LINESIZE+(0 div 4)=y*LINESIZE }
  4139.        SHL DI,1
  4140.        MOV DI,CS:[OFFSET GADR + DI]
  4141.  
  4142.        MOV AX,leftcut
  4143.        MOV BX,AX   {have a copy of leftcut in BX       }
  4144.        SHR AX,1
  4145.        SHR AX,1
  4146.        ADD SI,AX   {increment SI by cutoff (=leftcut div 4) bytes   }
  4147.  
  4148.        MOV CX,16
  4149.        SUB CX,bottomcut  {CX:=16-bottomcut = rows to draw        }
  4150.  
  4151.        {Now there is no further variable on stack, so BP can be used   }
  4152.        {for other purposes!}
  4153.        PUSH BP     {will be needed when leaving the procedure! }
  4154.        MOV BP,16+3
  4155.        SUB BP,BX   {BP:=16+3-leftcut, because the number of bytes per row  }
  4156.                    {of plane i is computed by (16+3-i-leftcut) SHR 2 }
  4157.  
  4158.  
  4159.        MOV ES,PAGEADR    {(segment part of) dest. address is active page}
  4160.        MOV DS,SCROLLADR  {(segment part of) source addr. is page SCROLLPAGE}
  4161.  
  4162.        MOV DX,3CEh
  4163.        AND BL,3
  4164.        MOV AH,BL   {AH:=leftcut mod 4}
  4165.        MOV AL,4
  4166.  
  4167.        JNE @mode0e {only if leftcut mod 4=0 is it possible to use WriteMode1      }
  4168.  
  4169.        {--- short-cut possible, using write mode1: DX=3CEh, BP=16+3-leftcut   }
  4170.        {    BP being "3 units to big" doesn't matter, because this code will  }
  4171.        {    only be run if leftcut mod 4=0, thus 16+3-leftcut=...11b, the     }
  4172.        {    "11b" will be cut off while shifting right, anyway!    }
  4173.        MOV AX,4105h
  4174.        OUT DX,AX   {choose write mode 1}
  4175.        MOV DX,3C4h
  4176.        MOV AX,0F02h    {work on all 4 planes simultaneously  }
  4177.        OUT DX,AX
  4178.  
  4179.        SHR BP,1      {use BP directly    }
  4180.        SHR BP,1      {BP:=bytes_per_plane3_row }
  4181.  
  4182.        MOV AX,LINESIZE {adjustment for row target addresses    }
  4183.        SUB AX,BP       { := LINESIZE-bytes_per_plane3_row }
  4184.        MOV DX,4        {adjustment for source addresses of the tiles}
  4185.        SUB DX,BP       { := 4-bytes_per_plane3_row }
  4186.  
  4187.        MOV BX,CX   {BX:=row counter  }
  4188.      @eineZeile4d1:
  4189.        MOV CX,BP   {CX:=bytes_per_plane0_row }
  4190.        REP MOVSB   {move data of one row  }
  4191.        ADD DI,AX   {set DI to next destination row  }
  4192.        ADD SI,DX   {set SI to next tile's source addr.   }
  4193.        DEC BX
  4194.        JNZ @eineZeile4d1
  4195.  
  4196.        {--- short cut has been taken: thus, reset write mode 0!     }
  4197.        MOV AX,4005h
  4198.        MOV DX,3CEh
  4199.        OUT DX,AX
  4200.        {---}
  4201.        JMP @LowerLeftTileDone
  4202.  
  4203.      @mode0e:
  4204.        OUT DX,AX   {choose plane from which to read}
  4205.        PUSH AX     {and preserve it for later}
  4206.  
  4207.        MOV DX,3C4h
  4208.        MOV AX,0102h    {choose write plane 0  }
  4209.        OUT DX,AX
  4210.  
  4211.        MOV BX,BP
  4212.        SHR BX,1
  4213.        SHR BX,1    {BX:=bytes_per_plane0_row = (16+3-leftcut) DIV 4 }
  4214.  
  4215.        MOV AX,LINESIZE {adjustment for row target addresses    }
  4216.        SUB AX,BX       { := LINESIZE-bytes_per_plane0_row }
  4217.        MOV DX,4        {adjustment for source addresses of the tiles}
  4218.        SUB DX,BX       { := 4-bytes_per_plane0_row }
  4219.  
  4220.        PUSH SI     {save source- and destination address for other planes}
  4221.        PUSH DI
  4222.        PUSH BP     {save BP  }
  4223.        PUSH CX     {CX = save row counter    }
  4224.        MOV BP,CX   {BP:=row counter  }
  4225.      @eineZeile1d:
  4226.        MOV CX,BX   {CX:=bytes_per_plane0_row }
  4227.        REP MOVSB   {move data of one row  }
  4228.        ADD DI,AX   {set DI to next destination row  }
  4229.        ADD SI,DX   {set SI to next tile's source addr.   }
  4230.        DEC BP
  4231.        JNZ @eineZeile1d
  4232.        POP CX
  4233.        POP BP
  4234.        POP DI
  4235.        POP SI
  4236.  
  4237.        MOV DX,3C4h
  4238.        MOV AX,0202h  {choose write plane 1  }
  4239.        OUT DX,AX
  4240.  
  4241.        MOV DX,3CEh   {next read plane:   }
  4242.        POP AX
  4243.        INC AH
  4244.        AND AH,3      {increment by 1 MOD 4}
  4245.        JNE @nowrap1e
  4246.        INC SI        {plane 0 follows after plane 3 again, but}
  4247.                      {the source address has increased by 1 byte }
  4248.      @nowrap1e:
  4249.        OUT DX,AX
  4250.        PUSH AX
  4251.  
  4252.  
  4253.        DEC BP        {BP:=16+2-leftcut}
  4254.        MOV BX,BP
  4255.        SHR BX,1
  4256.        SHR BX,1      {BX:=bytes_per_plane1_row }
  4257.  
  4258.        MOV AX,LINESIZE {adjustment for row target addresses    }
  4259.        SUB AX,BX       { := LINESIZE-bytes_per_plane1_row }
  4260.        MOV DX,4        {adjustment for source addresses of the tiles}
  4261.        SUB DX,BX       { := 4-bytes_per_plane1_row }
  4262.  
  4263.        PUSH SI     {save source- and destination address for other planes}
  4264.        PUSH DI
  4265.        PUSH BP     {save BP  }
  4266.        PUSH CX     {CX = save row counter    }
  4267.        MOV BP,CX   {BP:=row counter  }
  4268.      @eineZeile2d:
  4269.        MOV CX,BX   {CX:=bytes_per_plane0_row }
  4270.        REP MOVSB   {move data of one row  }
  4271.        ADD DI,AX   {set DI to next destination row  }
  4272.        ADD SI,DX   {set SI to next tile's source addr.   }
  4273.        DEC BP
  4274.        JNZ @eineZeile2d
  4275.        POP CX
  4276.        POP BP
  4277.        POP DI
  4278.        POP SI
  4279.  
  4280.        MOV DX,3C4h
  4281.        MOV AX,0402h  {choose write plane 2  }
  4282.        OUT DX,AX
  4283.  
  4284.        MOV DX,3CEh   {next read plane:   }
  4285.        POP AX
  4286.        INC AH
  4287.        AND AH,3      {increment by 1 MOD 4}
  4288.        JNE @nowrap2e
  4289.        INC SI        {plane 0 follows after plane 3 again, but}
  4290.                      {the source address has increased by 1 byte }
  4291.      @nowrap2e:
  4292.        OUT DX,AX
  4293.        PUSH AX
  4294.  
  4295.  
  4296.        DEC BP        {BP:=16+1-leftcut}
  4297.        MOV BX,BP
  4298.        SHR BX,1
  4299.        SHR BX,1      {BX:=bytes_per_plane2_row }
  4300.  
  4301.        MOV AX,LINESIZE {adjustment for row target addresses    }
  4302.        SUB AX,BX       { := LINESIZE-bytes_per_plane2_row }
  4303.        MOV DX,4        {adjustment for source addresses of the tiles}
  4304.        SUB DX,BX       { := 4-bytes_per_plane2_row }
  4305.  
  4306.        PUSH SI     {save source- and destination address for other planes}
  4307.        PUSH DI
  4308.        PUSH BP     {save BP  }
  4309.        PUSH CX     {CX = save row counter    }
  4310.        MOV BP,CX   {BP:=row counter  }
  4311.      @eineZeile3d:
  4312.        MOV CX,BX   {CX:=bytes_per_plane0_row }
  4313.        REP MOVSB   {move data of one row  }
  4314.        ADD DI,AX   {set DI to next destination row  }
  4315.        ADD SI,DX   {set SI to next tile's source addr.   }
  4316.        DEC BP
  4317.        JNZ @eineZeile3d
  4318.        POP CX
  4319.        POP BP
  4320.        POP DI
  4321.        POP SI
  4322.  
  4323.        MOV DX,3C4h
  4324.        MOV AX,0802h  {choose write plane 3  }
  4325.        OUT DX,AX
  4326.  
  4327.        MOV DX,3CEh   {next read plane:   }
  4328.        POP AX
  4329.        INC AH
  4330.        AND AH,3      {increment by 1 MOD 4}
  4331.        JNE @nowrap3e
  4332.        INC SI        {plane 0 follows after plane 3 again, but}
  4333.                      {the source address has increased by 1 byte }
  4334.      @nowrap3e:
  4335.        OUT DX,AX
  4336.                      {do not push value again!}
  4337.  
  4338.  
  4339.        DEC BP        {BP:=16-leftcut}
  4340.      @lastplane5:
  4341.                      
  4342.        SHR BP,1      {use BP directly    }
  4343.        SHR BP,1      {BP:=bytes_per_plane3_row }
  4344.  
  4345.        MOV AX,LINESIZE {adjustment for row target addresses    }
  4346.        SUB AX,BP       { := LINESIZE-bytes_per_plane3_row }
  4347.        MOV DX,4        {adjustment for source addresses of the tiles}
  4348.        SUB DX,BP       { := 4-bytes_per_plane3_row }
  4349.  
  4350.        MOV BX,CX   {BX:=row counter  }
  4351.      @eineZeile4d:
  4352.        MOV CX,BP   {CX:=bytes_per_plane0_row }
  4353.        REP MOVSB   {move data of one row  }
  4354.        ADD DI,AX   {set DI to next destination row  }
  4355.        ADD SI,DX   {set SI to next tile's source addr.   }
  4356.        DEC BX
  4357.        JNZ @eineZeile4d
  4358.  
  4359.       @LowerLeftTileDone:
  4360.        POP BP
  4361.        MOV AX,SEG @Data  {restore DS         }
  4362.        MOV DS,AX
  4363.  
  4364.   @label2:
  4365.     MOV AX,xpix
  4366.     MOV x,AX
  4367.  
  4368.     {Now the lower tiles, which are not partially cut at their left or right:}
  4369.     @repeat4:
  4370.       MOV CL,offscreenFlag
  4371.       XOR SI,SI
  4372.       DEC CL
  4373.       JZ @go8
  4374.       MOV AX,xt
  4375.       OR AX,AX
  4376.       JS @go8
  4377.       CMP AX,XTiles
  4378.       JAE @go8
  4379.       MOV SI,actIndex
  4380.     @go8:
  4381.  
  4382.          {PROCEDURE DrawLowerTile(x,y,bottomcut:INTEGER; index:WORD);}
  4383.          { in: (x,y) = upper left corner of the tile to be drawn}
  4384.          {     bottomcut = number of bottom tile-rows to be cut off     }
  4385.          {     SI = index = tile number}
  4386.          {out: tile has been drawn on actual page PAGEADR       }
  4387.          {rem: tile has been clipped at its bottom accordingly}
  4388.          {     bottomcut must lie in the range 0..15, 16 is not allowed!}
  4389.          {     bottomcut could also be computed by:             }
  4390.          {     bottomcut:=y+15-ymax}
  4391.          MOV AH,[OFFSET BackTile +SI]  {AH:=BackTile[index]}
  4392.          XOR AL,AL
  4393.  
  4394.          SHR AH,1    {AX:=tile*64 =tile SHL 6 =(tile SHL 8) SHR 2      }
  4395.          RCR AL,1    {therefore: set AH:=tile and afterwards, shift AX }
  4396.          SHR AH,1    {2 bits to the right!}
  4397.          RCR AL,1
  4398.          MOV SI,AX   {That's also the offset part of the source ad. in page3}
  4399.  
  4400.          MOV DI,y    {the first destination addr. is DI:=y*LINESIZE+(x div 4)}
  4401.          SHL DI,1
  4402.          MOV DI,CS:[OFFSET GADR + DI]
  4403.          MOV AX,x
  4404.          MOV BX,AX   {have a copy of X in BX       }
  4405.          SHR AX,1
  4406.          SHR AX,1
  4407.          ADD DI,AX
  4408.  
  4409.          MOV CX,16
  4410.          SUB CX,bottomcut  {CX:=number of rows to draw = 16-bottomcut  }
  4411.  
  4412.          {Now there is no further variable on stack, so BP can be used   }
  4413.          {for other purposes!}
  4414.          PUSH BP     {will be needed when leaving the procedure! }
  4415.          MOV BP,CX
  4416.          SHL BP,1
  4417.          SHL BP,1    {BP := (rows to draw)*4         }
  4418.  
  4419.          MOV ES,PAGEADR    {(segment part of) dest. address is active page}
  4420.          MOV DS,SCROLLADR  {(segment part of) source addr. is page SCROLLPAGE}
  4421.  
  4422.          MOV DX,3C4h
  4423.          MOV AL,2
  4424.          AND BX,3    {BX:=1st write plane (1st READ plane=0!)   }
  4425.          JNE @mode0f {only if x mod 4=0 is it possible to use WriteMode1      }
  4426.  
  4427.          {--- short-cut possible, using write mode1   }
  4428.          MOV AH,0Fh  {work on all 4 planes simultaneously  }
  4429.          OUT DX,AX
  4430.          MOV AX,4105h
  4431.          MOV DX,3CEh
  4432.          OUT DX,AX   {choose write mode 1}
  4433.          MOV BX,CX   {BX:=CX=rows to draw        }
  4434.  
  4435.          MOV AX,LINESIZE-4
  4436.          MOV CX,BX   {CX:=number of rows to draw  }
  4437.        @eineZeile4e1:
  4438.          MOVSB       {no "MOVSW", as we use write mode1}
  4439.          MOVSB
  4440.          MOVSB
  4441.          MOVSB
  4442.          ADD DI,AX   {set DI to next row          }
  4443.          LOOP @eineZeile4e1
  4444.  
  4445.          {--- short cut has been taken: thus, reset write mode 0!     }
  4446.          MOV AX,4005h
  4447.          MOV DX,3CEh
  4448.          OUT DX,AX
  4449.          {---}
  4450.          JMP @LowerTileDone
  4451.  
  4452.        @mode0f:
  4453.          MOV AH,CS:[OFFSET CS_TranslateTab+BX]
  4454.          OUT DX,AX
  4455.          PUSH AX     {save actual write plane     }
  4456.  
  4457.          MOV DX,3CEh
  4458.          MOV AX,0004h    {choose plane 0 for reading  }
  4459.          OUT DX,AX
  4460.  
  4461.          MOV AX,LINESIZE-4 {adjustment for row addresses       }
  4462.          MOV BX,CX   {save number of rows to draw into BX    }
  4463.        @eineZeile1e:
  4464.          MOVSW
  4465.          MOVSW
  4466.          ADD DI,AX   {set DI to next row          }
  4467.          LOOP @eineZeile1e
  4468.  
  4469.          MOV AX,0104h  {DX=3CEh -> choose read plane 1}
  4470.          OUT DX,AX
  4471.  
  4472.          MOV DX,3C4h   {next write plane:     }
  4473.          POP AX
  4474.          SHL AH,1
  4475.          CMP AH,16
  4476.          JNE @nowrap1f
  4477.          MOV AH,1      {plane 0 follows after plane 3 again, but}
  4478.          INC DI        {the destination address has increased by 1 byte}
  4479.        @nowrap1f:
  4480.          OUT DX,AX
  4481.          PUSH AX
  4482.          SHL BX,1      {reset DI to the source address:              }
  4483.          SUB DI,CS:[OFFSET GADR + BX] {thus, decrement DI by (rows to draw)*     }
  4484.          SHR BX,1      {LINESIZE (N.B.: BX=number of rows to draw)}
  4485.          SUB SI,BP     {reset SI, too        }
  4486.  
  4487.  
  4488.          MOV AX,LINESIZE-4
  4489.          MOV CX,BX   {CX:=number of rows to draw    }
  4490.        @eineZeile2e:
  4491.          MOVSW
  4492.          MOVSW
  4493.          ADD DI,AX   {set DI to next row          }
  4494.          LOOP @eineZeile2e
  4495.  
  4496.          POP AX
  4497.          SHL AH,1
  4498.          CMP AH,16
  4499.          JNE @nowrap2f
  4500.          MOV AH,1      {plane 0 follows after plane 3 again, but}
  4501.          INC DI        {the destination address has increased by 1 byte}
  4502.        @nowrap2f:
  4503.          OUT DX,AX     {DX=3C4h -> set write plane    }
  4504.          PUSH AX
  4505.          MOV DX,3CEh
  4506.          MOV AX,0204h  {set read plane 2  }
  4507.          OUT DX,AX
  4508.          SHL BX,1      {reset DI to the source address:              }
  4509.          SUB DI,CS:[OFFSET GADR + BX] {thus, decrement DI by (rows to draw)*     }
  4510.          SHR BX,1      {LINESIZE (N.B.: BX=number of rows to draw)}
  4511.          SUB SI,BP     {reset SI, too        }
  4512.  
  4513.  
  4514.          MOV AX,LINESIZE-4
  4515.          MOV CX,BX   {CX:=number of rows to draw  }
  4516.        @eineZeile3e:
  4517.          MOVSW
  4518.          MOVSW
  4519.          ADD DI,AX   {set DI to next row          }
  4520.          LOOP @eineZeile3e
  4521.  
  4522.          MOV AX,0304h
  4523.          OUT DX,AX     {DX=3CEh -> choose read plane 3}
  4524.          MOV DX,3C4h
  4525.          POP AX
  4526.          SHL AH,1
  4527.          CMP AH,16
  4528.          JNE @nowrap3f
  4529.          MOV AH,1      {plane 0 follows after plane 3 again, but}
  4530.          INC DI        {the destination address has increased by 1 byte}
  4531.        @nowrap3f:
  4532.          OUT DX,AX     {DX=3C4h -> set write plane    }
  4533.          SHL BX,1      {reset DI to the source address:              }
  4534.          SUB DI,CS:[OFFSET GADR + BX] {thus, decrement DI by (rows to draw)*     }
  4535.          SHR BX,1      {LINESIZE (N.B.: BX=number of rows to draw)}
  4536.          SUB SI,BP     {reset SI, too        }
  4537.  
  4538.  
  4539.        @lastPlane6:
  4540.          MOV AX,LINESIZE-4
  4541.          MOV CX,BX   {CX:=number of rows to draw  }
  4542.        @eineZeile4e:
  4543.          MOVSW
  4544.          MOVSW
  4545.          ADD DI,AX   {set DI to next row          }
  4546.          LOOP @eineZeile4e
  4547.  
  4548.        @LowerTileDone:
  4549.          POP BP
  4550.          MOV AX,SEG @Data  {restore DS         }
  4551.          MOV DS,AX
  4552.  
  4553.     INC xt
  4554.     INC actIndex
  4555.     MOV AX,x
  4556.     ADD AX,16
  4557.     MOV x,AX
  4558.     CMP AX,XMAX+1-16
  4559.     JBE @repeat4
  4560.  
  4561.     {Now evtl. the lower right corner-tile remains to be drawn:}
  4562.     CMP AX,XMAX+1
  4563.     JE @do_raender
  4564.     MOV CL,offscreenFlag
  4565.     XOR SI,SI
  4566.     DEC CL
  4567.     JZ @go9
  4568.     MOV AX,xt
  4569.     OR AX,AX
  4570.     JS @go9
  4571.     CMP AX,XTiles
  4572.     JAE @go9
  4573.     MOV SI,actIndex
  4574.   @go9:
  4575.  
  4576.    {PROCEDURE DrawLowerRightTile(x,y,rightcut,bottomcut:INTEGER; index:WORD);}
  4577.    { in: (x,y) = upper left corner of the tile to be drawn}
  4578.    {     rightcut = number of right tile-columns to be cut off     }
  4579.    {     bottomcut = dto., bottom}
  4580.    {     bottomcut must lie in the range 0..15, 16 is not allowed}
  4581.    {     bottomcut could also be computed by:             }
  4582.    {     bottomcut:=y+15-ymax}
  4583.    {     SI = index = tile number}
  4584.    {out: tile has been drawn on actual page PAGEADR       }
  4585.    {rem: tile has been clipped at its right and bottom accordingly}
  4586.    {     rightcut must lie between 0..15, 16 is allowed (but senseless)!        }
  4587.    {     rightcut could also be computed (for x>xmax-16) by             }
  4588.    {     rightcut := x+15-xmax }
  4589.    {     bottomcut must lie in the range 0..15, 16 is not allowed}
  4590.    {     bottomcut could also be computed by:             }
  4591.    {     bottomcut:=y+15-ymax}
  4592.        MOV AH,[OFFSET BackTile +SI]  {AH:=BackTile[index]}
  4593.        XOR AL,AL
  4594.  
  4595.        SHR AH,1    {AX:=tile*64 =tile SHL 6 =(tile SHL 8) SHR 2      }
  4596.        RCR AL,1    {therefore: set AH:=tile and afterwards, shift AX }
  4597.        SHR AH,1    {2 bits to the right!}
  4598.        RCR AL,1
  4599.        MOV SI,AX   {That's also the offset part of the source ad. in page3}
  4600.  
  4601.        MOV DI,y    {the first destination addr. is DI:=y*LINESIZE+(x div 4)}
  4602.        SHL DI,1
  4603.        MOV DI,CS:[OFFSET GADR + DI]
  4604.        MOV AX,x
  4605.        MOV BX,AX   {place a copy of x in BX}
  4606.        SHR AX,1
  4607.        SHR AX,1
  4608.        ADD DI,AX
  4609.  
  4610.        MOV AX,rightcut
  4611.        MOV CX,16
  4612.        SUB CX,bottomcut  {CX:=16-bottomcut = rows to draw        }
  4613.  
  4614.        {Now there is no further variable on stack, so BP can be used   }
  4615.        {for other purposes!}
  4616.        PUSH BP     {will be needed when leaving the procedure! }
  4617.        MOV BP,16+3
  4618.        SUB BP,AX   {BP:=16+3-rightcut, because the number of bytes per row  }
  4619.                    {of plane i is computed by (16+3-i-rightcut) SHR 2 }
  4620.        MOV AH,AL   {save rightcut to AH    }
  4621.  
  4622.  
  4623.        MOV ES,PAGEADR    {(segment part of) dest. address is active page}
  4624.        MOV DS,SCROLLADR  {(segment part of) source addr. is page SCROLLPAGE}
  4625.  
  4626.        MOV DX,3C4h
  4627.        MOV AL,2
  4628.        AND BX,3
  4629.  
  4630.        JNE @mode0g {only if x mod 4=0 _and_ rightcut mod 4=0, then we can }
  4631.        AND AH,3    {use write mode 1!        }
  4632.        JNE @mode0g
  4633.  
  4634.        {--- short-cut possible, using write mode1: DX=3C4h, BP=16+3-rightcut   }
  4635.        MOV AH,0Fh
  4636.        OUT DX,AX    {work on all 4 planes simultaneously  }
  4637.        MOV DX,3CEh
  4638.        MOV AX,4105h {choose write mode 1}
  4639.        OUT DX,AX
  4640.        SUB BP,3     {set BP to proper size              }
  4641.  
  4642.        SHR BP,1      {use BP directly    }
  4643.        SHR BP,1      {BX:=bytes_per_plane3_row }
  4644.  
  4645.        MOV AX,LINESIZE {adjustment for row target addresses    }
  4646.        SUB AX,BP       { := LINESIZE-bytes_per_plane3_row }
  4647.        MOV DX,4        {adjustment for source addresses of the tiles}
  4648.        SUB DX,BP       { := 4-bytes_per_plane3_row }
  4649.  
  4650.        MOV BX,CX   {BX:=row counter  }
  4651.      @eineZeile4g1:
  4652.        MOV CX,BP   {CX:=bytes_per_plane0_row }
  4653.        REP MOVSB   {move data of one row  }
  4654.        ADD DI,AX   {set DI to next destination row  }
  4655.        ADD SI,DX   {set SI to next tile's source addr.   }
  4656.        DEC BX
  4657.        JNZ @eineZeile4g1
  4658.  
  4659.        {--- short cut has been taken: thus, reset write mode 0!     }
  4660.        MOV AX,4005h
  4661.        MOV DX,3CEh
  4662.        OUT DX,AX
  4663.        {---}
  4664.        JMP @LowerRightTileDone
  4665.  
  4666.      @mode0g:
  4667.        MOV AH,CS:[OFFSET CS_TranslateTab +BX]
  4668.        OUT DX,AX   {choose plane to write at     }
  4669.        PUSH AX     {and preserve it for later}
  4670.  
  4671.        MOV DX,3CEh
  4672.        MOV AX,0004h    {choose read plane 0}
  4673.        OUT DX,AX
  4674.  
  4675.        MOV BX,BP
  4676.        SHR BX,1
  4677.        SHR BX,1    {BX:=bytes_per_plane0_row = (16+3-rightcut) DIV 4 }
  4678.  
  4679.        MOV AX,LINESIZE {adjustment for row target addresses    }
  4680.        SUB AX,BX       { := LINESIZE-bytes_per_plane0_row }
  4681.        MOV DX,4        {adjustment for source addresses of the tiles}
  4682.        SUB DX,BX       { := 4-bytes_per_plane0_row }
  4683.  
  4684.        PUSH SI     {save source- and destination address for other planes}
  4685.        PUSH DI
  4686.        PUSH BP     {save BP  }
  4687.        PUSH CX     {CX = save row counter    }
  4688.        MOV BP,CX   {BP:=row counter  }
  4689.      @eineZeile1f:
  4690.        MOV CX,BX   {CX:=bytes_per_plane0_row }
  4691.        REP MOVSB   {move data of one row  }
  4692.        ADD DI,AX   {set DI to next destination row  }
  4693.        ADD SI,DX   {set SI to next tile's source addr.   }
  4694.        DEC BP
  4695.        JNZ @eineZeile1f
  4696.        POP CX
  4697.        POP BP
  4698.        POP DI
  4699.        POP SI
  4700.  
  4701.  
  4702.        MOV DX,3CEh
  4703.        MOV AX,0104h  {choose read plane 1}
  4704.        OUT DX,AX
  4705.  
  4706.        MOV DX,3C4h   {next write plane:     }
  4707.        POP AX
  4708.        SHL AH,1
  4709.        CMP AH,16
  4710.        JNE @nowrap1g
  4711.        MOV AH,1      {plane 0 follows after plane 3 again, but}
  4712.        INC DI        {the destination address has increased by 1 byte}
  4713.      @nowrap1g:
  4714.        OUT DX,AX
  4715.        PUSH AX
  4716.  
  4717.  
  4718.        DEC BP        {BP:=16+2-rightcut}
  4719.        MOV BX,BP
  4720.        SHR BX,1
  4721.        SHR BX,1      {BX:=bytes_per_plane1_row }
  4722.  
  4723.        MOV AX,LINESIZE {adjustment for row target addresses    }
  4724.        SUB AX,BX       { := LINESIZE-bytes_per_plane1_row }
  4725.        MOV DX,4        {adjustment for source addresses of the tiles}
  4726.        SUB DX,BX       { := 4-bytes_per_plane1_row }
  4727.  
  4728.        PUSH SI     {save source- and destination address for other planes}
  4729.        PUSH DI
  4730.        PUSH BP     {save BP  }
  4731.        PUSH CX     {CX = save row counter    }
  4732.        MOV BP,CX   {BP:=row counter  }
  4733.      @eineZeile2f:
  4734.        MOV CX,BX   {CX:=bytes_per_plane0_row }
  4735.        REP MOVSB   {move data of one row  }
  4736.        ADD DI,AX   {set DI to next destination row  }
  4737.        ADD SI,DX   {set SI to next tile's source addr.   }
  4738.        DEC BP
  4739.        JNZ @eineZeile2f
  4740.        POP CX
  4741.        POP BP
  4742.        POP DI
  4743.        POP SI
  4744.  
  4745.  
  4746.        MOV DX,3CEh
  4747.        MOV AX,0204h  {choose read plane 2}
  4748.        OUT DX,AX
  4749.  
  4750.        MOV DX,3C4h   {next write plane:     }
  4751.        POP AX
  4752.        SHL AH,1
  4753.        CMP AH,16
  4754.        JNE @nowrap2g
  4755.        MOV AH,1      {plane 0 follows after plane 3 again, but}
  4756.        INC DI        {the destination address has increased by 1 byte}
  4757.      @nowrap2g:
  4758.        OUT DX,AX
  4759.        PUSH AX
  4760.  
  4761.  
  4762.        DEC BP        {BP:=16+1-rightcut}
  4763.        MOV BX,BP
  4764.        SHR BX,1
  4765.        SHR BX,1      {BX:=bytes_per_plane2_row }
  4766.  
  4767.        MOV AX,LINESIZE {adjustment for row target addresses    }
  4768.        SUB AX,BX       { := LINESIZE-bytes_per_plane2_row }
  4769.        MOV DX,4        {adjustment for source addresses of the tiles}
  4770.        SUB DX,BX       { := 4-bytes_per_plane2_row }
  4771.  
  4772.        PUSH SI     {save source- and destination address for other planes}
  4773.        PUSH DI
  4774.        PUSH BP     {save BP  }
  4775.        PUSH CX     {CX = save row counter    }
  4776.        MOV BP,CX   {BP:=row counter  }
  4777.      @eineZeile3f:
  4778.        MOV CX,BX   {CX:=bytes_per_plane0_row }
  4779.        REP MOVSB   {move data of one row  }
  4780.        ADD DI,AX   {set DI to next destination row  }
  4781.        ADD SI,DX   {set SI to next tile's source addr.   }
  4782.        DEC BP
  4783.        JNZ @eineZeile3f
  4784.        POP CX
  4785.        POP BP
  4786.        POP DI
  4787.        POP SI
  4788.  
  4789.  
  4790.        MOV DX,3CEh
  4791.        MOV AX,0304h  {choose read plane 3}
  4792.        OUT DX,AX
  4793.  
  4794.        MOV DX,3C4h   {next write plane:     }
  4795.        POP AX
  4796.        SHL AH,1
  4797.        CMP AH,16
  4798.        JNE @nowrap3g
  4799.        MOV AH,1      {plane 0 follows after plane 3 again, but}
  4800.        INC DI        {the destination address has increased by 1 byte}
  4801.      @nowrap3g:
  4802.        OUT DX,AX
  4803.                      {do not push value again!}
  4804.  
  4805.  
  4806.        DEC BP        {BP:=16-rightcut}
  4807.      @lastplane7:
  4808.                      
  4809.        SHR BP,1      {use BP directly    }
  4810.        SHR BP,1      {BX:=bytes_per_plane3_row }
  4811.  
  4812.        MOV AX,LINESIZE {adjustment for row target addresses    }
  4813.        SUB AX,BP       { := LINESIZE-bytes_per_plane3_row }
  4814.        MOV DX,4        {adjustment for source addresses of the tiles}
  4815.        SUB DX,BP       { := 4-bytes_per_plane3_row }
  4816.  
  4817.        MOV BX,CX   {BX:=row counter  }
  4818.      @eineZeile4g:
  4819.        MOV CX,BP   {CX:=bytes_per_plane0_row }
  4820.        REP MOVSB   {move data of one row  }
  4821.        ADD DI,AX   {set DI to next destination row  }
  4822.        ADD SI,DX   {set SI to next tile's source addr.   }
  4823.        DEC BX
  4824.        JNZ @eineZeile4g
  4825.  
  4826.      @LowerRightTileDone:
  4827.        POP BP
  4828.        MOV AX,SEG @Data  {restore DS         }
  4829.        MOV DS,AX
  4830.  
  4831.   {Now work on the tiles at the left and/or right edge of the screen:      }
  4832.   @do_raender:
  4833.     CMP leftcut,0  {or "rightcut": because 320 is dividable by 16,  }
  4834.     JE @ende       {leftcut=0 is equivalent to rightcut=0 }
  4835.  
  4836.     MOV AX,offsetXTiles
  4837.     DEC AX
  4838.     MOV xt,AX
  4839.     MOV AX,ypix
  4840.     MOV y,AX
  4841.     INC tiles
  4842.     MOV AX,offsetYTiles
  4843.     MOV yt,AX
  4844.  
  4845.   @repeat5:
  4846.     MOV CL,1
  4847.     OR AX,AX
  4848.     JS @go10
  4849.     CMP AX,YTiles
  4850.     JAE @go10
  4851.     DEC CL
  4852.   @go10:
  4853.     MOV offscreenFlag,CL
  4854.     XOR SI,SI
  4855.     DEC CL
  4856.     JZ @go11
  4857.     MOV AX,xt
  4858.     OR AX,AX
  4859.     JS @go11
  4860.     CMP AX,XTiles
  4861.     JAE @go11
  4862.     MOV SI,RandIndex
  4863.   @go11:
  4864.  
  4865.  
  4866.        {PROCEDURE DrawLeftTile(y,leftcut:INTEGER; index:WORD);}
  4867.        { in: (0,y) = upper left corner of the tile to be drawn}
  4868.        {     leftcut = number of left tile-columns to be cut off     }
  4869.        {     SI = index = tile number}
  4870.        {out: tile has been drawn on actual page PAGEADR       }
  4871.        {rem: tile has been clipped at its left accordingly}
  4872.        {     leftcut must lie in the range 0..15, 16 is allowed (but senseless)   }
  4873.        MOV AH,[OFFSET BackTile +SI]  {AH:=BackTile[index]}
  4874.        XOR AL,AL
  4875.  
  4876.        SHR AH,1    {AX:=tile*64 =tile SHL 6 =(tile SHL 8) SHR 2      }
  4877.        RCR AL,1    {therefore: set AH:=tile and afterwards, shift AX }
  4878.        SHR AH,1    {2 bits to the right!}
  4879.        RCR AL,1
  4880.        MOV SI,AX   {That's also the offset part of the source ad. in page3}
  4881.  
  4882.        MOV DI,y    {first dest. addr. is DI:=y*LINESIZE+(0 div 4)=y*LINESIZE }
  4883.        SHL DI,1
  4884.        MOV DI,CS:[OFFSET GADR + DI]
  4885.  
  4886.        MOV AX,leftcut
  4887.        MOV BX,AX   {have a copy of leftcut in BX       }
  4888.        SHR AX,1
  4889.        SHR AX,1
  4890.        ADD SI,AX   {increment SI by cutoff (=leftcut div 4) bytes   }
  4891.  
  4892.        {Now there is no further variable on stack, so BP can be used   }
  4893.        {for other purposes!}
  4894.        PUSH BP     {will be needed when leaving the procedure! }
  4895.        MOV BP,16+3
  4896.        SUB BP,BX   {BP:=16+3-leftcut, because the number of bytes per row  }
  4897.                    {of plane i is computed by (16+3-i-leftcut) SHR 2 }
  4898.  
  4899.  
  4900.        MOV ES,PAGEADR    {(segment part of) dest. address is active page}
  4901.        MOV DS,SCROLLADR  {(segment part of) source addr. is page SCROLLPAGE}
  4902.  
  4903.        MOV DX,3CEh
  4904.        AND BL,3
  4905.        MOV AH,BL   {AH:=leftcut mod 4}
  4906.        MOV AL,4
  4907.  
  4908.        JNE @mode0h {only if leftcut mod 4=0 is it possible to use WriteMode1      }
  4909.  
  4910.        {--- short-cut possible, using write mode1: DX=3CEh, BP=16+3-leftcut   }
  4911.        {    BP being "3 units to big" doesn't matter, because this code will  }
  4912.        {    only be run if leftcut mod 4=0, thus 16+3-leftcut=...11b, the     }
  4913.        {    "11b" will be cut off while shifting right, anyway!    }
  4914.        MOV AX,4105h
  4915.        OUT DX,AX   {choose write mode 1}
  4916.        MOV DX,3C4h
  4917.        MOV AX,0F02h    {work on all 4 planes simultaneously  }
  4918.        OUT DX,AX
  4919.  
  4920.        SHR BP,1      {use BP directly    }
  4921.        SHR BP,1      {BP:=bytes_per_plane3_row }
  4922.  
  4923.        MOV AX,LINESIZE {adjustment for row target addresses    }
  4924.        SUB AX,BP       { := LINESIZE-bytes_per_plane3_row }
  4925.        MOV DX,4        {adjustment for source addresses of the tiles}
  4926.        SUB DX,BP       { := 4-bytes_per_plane3_row }
  4927.  
  4928.        MOV CX,BP   {CX:=bytes_per_plane2_row }
  4929.        REP MOVSB   {1.row  }
  4930.        ADD DI,AX   {set DI to next destination row  }
  4931.        ADD SI,DX   {set SI to next tile's source addr.   }
  4932.        MOV CX,BP
  4933.        REP MOVSB   {2.row  }
  4934.        ADD DI,AX
  4935.        ADD SI,DX
  4936.        MOV CX,BP
  4937.        REP MOVSB   {3.row  }
  4938.        ADD DI,AX
  4939.        ADD SI,DX
  4940.        MOV CX,BP
  4941.        REP MOVSB   {4.row  }
  4942.        ADD DI,AX
  4943.        ADD SI,DX
  4944.        MOV CX,BP
  4945.        REP MOVSB   {5.row  }
  4946.        ADD DI,AX
  4947.        ADD SI,DX
  4948.        MOV CX,BP
  4949.        REP MOVSB   {6.row  }
  4950.        ADD DI,AX
  4951.        ADD SI,DX
  4952.        MOV CX,BP
  4953.        REP MOVSB   {7.row  }
  4954.        ADD DI,AX
  4955.        ADD SI,DX
  4956.        MOV CX,BP
  4957.        REP MOVSB   {8.row  }
  4958.        ADD DI,AX
  4959.        ADD SI,DX
  4960.        MOV CX,BP
  4961.        REP MOVSB   {9.row  }
  4962.        ADD DI,AX
  4963.        ADD SI,DX
  4964.        MOV CX,BP
  4965.        REP MOVSB   {10.row  }
  4966.        ADD DI,AX
  4967.        ADD SI,DX
  4968.        MOV CX,BP
  4969.        REP MOVSB   {11.row  }
  4970.        ADD DI,AX
  4971.        ADD SI,DX
  4972.        MOV CX,BP
  4973.        REP MOVSB   {12.row  }
  4974.        ADD DI,AX
  4975.        ADD SI,DX
  4976.        MOV CX,BP
  4977.        REP MOVSB   {13.row  }
  4978.        ADD DI,AX
  4979.        ADD SI,DX
  4980.        MOV CX,BP
  4981.        REP MOVSB   {14.row  }
  4982.        ADD DI,AX
  4983.        ADD SI,DX
  4984.        MOV CX,BP
  4985.        REP MOVSB   {15.row  }
  4986.        ADD DI,AX
  4987.        ADD SI,DX
  4988.        MOV CX,BP
  4989.        REP MOVSB   {16.row  }
  4990.  
  4991.        {--- short cut has been taken: thus, reset write mode 0!     }
  4992.        MOV AX,4005h
  4993.        MOV DX,3CEh
  4994.        OUT DX,AX
  4995.        {---}
  4996.        JMP @LeftTileDone
  4997.  
  4998.      @mode0h:
  4999.        OUT DX,AX   {choose plane from which to read}
  5000.        PUSH AX     {and preserve it for later}
  5001.  
  5002.        MOV DX,3C4h
  5003.        MOV AX,0102h    {choose write plane 0  }
  5004.        OUT DX,AX
  5005.  
  5006.        MOV BX,BP
  5007.        SHR BX,1
  5008.        SHR BX,1    {BX:=bytes_per_plane0_row = (16+3-leftcut) DIV 4 }
  5009.  
  5010.        MOV AX,LINESIZE {adjustment for row target addresses    }
  5011.        SUB AX,BX       { := LINESIZE-bytes_per_plane0_row }
  5012.        MOV DX,4        {adjustment for source addresses of the tiles}
  5013.        SUB DX,BX       { := 4-bytes_per_plane0_row }
  5014.  
  5015.        MOV CX,BX   {CX:=bytes_per_plane0_row }
  5016.        REP MOVSB   {1.row  }
  5017.        ADD DI,AX   {set DI to next destination row  }
  5018.        ADD SI,DX   {set SI to next tile's source addr.   }
  5019.        MOV CX,BX
  5020.        REP MOVSB   {2.row  }
  5021.        ADD DI,AX
  5022.        ADD SI,DX
  5023.        MOV CX,BX
  5024.        REP MOVSB   {3.row  }
  5025.        ADD DI,AX
  5026.        ADD SI,DX
  5027.        MOV CX,BX
  5028.        REP MOVSB   {4.row  }
  5029.        ADD DI,AX
  5030.        ADD SI,DX
  5031.        MOV CX,BX
  5032.        REP MOVSB   {5.row  }
  5033.        ADD DI,AX
  5034.        ADD SI,DX
  5035.        MOV CX,BX
  5036.        REP MOVSB   {6.row  }
  5037.        ADD DI,AX
  5038.        ADD SI,DX
  5039.        MOV CX,BX
  5040.        REP MOVSB   {7.row  }
  5041.        ADD DI,AX
  5042.        ADD SI,DX
  5043.        MOV CX,BX
  5044.        REP MOVSB   {8.row  }
  5045.        ADD DI,AX
  5046.        ADD SI,DX
  5047.        MOV CX,BX
  5048.        REP MOVSB   {9.row  }
  5049.        ADD DI,AX
  5050.        ADD SI,DX
  5051.        MOV CX,BX
  5052.        REP MOVSB   {10.row  }
  5053.        ADD DI,AX
  5054.        ADD SI,DX
  5055.        MOV CX,BX
  5056.        REP MOVSB   {11.row  }
  5057.        ADD DI,AX
  5058.        ADD SI,DX
  5059.        MOV CX,BX
  5060.        REP MOVSB   {12.row  }
  5061.        ADD DI,AX
  5062.        ADD SI,DX
  5063.        MOV CX,BX
  5064.        REP MOVSB   {13.row  }
  5065.        ADD DI,AX
  5066.        ADD SI,DX
  5067.        MOV CX,BX
  5068.        REP MOVSB   {14.row  }
  5069.        ADD DI,AX
  5070.        ADD SI,DX
  5071.        MOV CX,BX
  5072.        REP MOVSB   {15.row  }
  5073.        ADD DI,AX
  5074.        ADD SI,DX
  5075.        MOV CX,BX
  5076.        REP MOVSB   {16.row  }
  5077.        ADD DI,AX
  5078.        ADD SI,DX
  5079.  
  5080.  
  5081.        MOV DX,3C4h
  5082.        MOV AX,0202h  {choose write plane 1  }
  5083.        OUT DX,AX
  5084.  
  5085.        MOV DX,3CEh   {next read plane:   }
  5086.        POP AX
  5087.        INC AH
  5088.        AND AH,3      {increment by 1 MOD 4}
  5089.        JNE @nowrap1h
  5090.        INC SI        {plane 0 follows after plane 3 again, but}
  5091.                      {the source address has increased by 1 byte }
  5092.      @nowrap1h:
  5093.        OUT DX,AX
  5094.        PUSH AX
  5095.        SUB DI,16*LINESIZE   {reset DI and SI registers     }
  5096.        SUB SI,16*4
  5097.  
  5098.  
  5099.        DEC BP        {BP:=16+2-leftcut}
  5100.        MOV BX,BP
  5101.        SHR BX,1
  5102.        SHR BX,1      {BX:=bytes_per_plane1_row }
  5103.  
  5104.        MOV AX,LINESIZE {adjustment for row target addresses    }
  5105.        SUB AX,BX       { := LINESIZE-bytes_per_plane1_row }
  5106.        MOV DX,4        {adjustment for source addresses of the tiles}
  5107.        SUB DX,BX       { := 4-bytes_per_plane1_row }
  5108.  
  5109.        MOV CX,BX   {CX:=bytes_per_plane1_row }
  5110.        REP MOVSB   {1.row  }
  5111.        ADD DI,AX   {set DI to next destination row  }
  5112.        ADD SI,DX   {set SI to next tile's source addr.   }
  5113.        MOV CX,BX
  5114.        REP MOVSB   {2.row  }
  5115.        ADD DI,AX
  5116.        ADD SI,DX
  5117.        MOV CX,BX
  5118.        REP MOVSB   {3.row  }
  5119.        ADD DI,AX
  5120.        ADD SI,DX
  5121.        MOV CX,BX
  5122.        REP MOVSB   {4.row  }
  5123.        ADD DI,AX
  5124.        ADD SI,DX
  5125.        MOV CX,BX
  5126.        REP MOVSB   {5.row  }
  5127.        ADD DI,AX
  5128.        ADD SI,DX
  5129.        MOV CX,BX
  5130.        REP MOVSB   {6.row  }
  5131.        ADD DI,AX
  5132.        ADD SI,DX
  5133.        MOV CX,BX
  5134.        REP MOVSB   {7.row  }
  5135.        ADD DI,AX
  5136.        ADD SI,DX
  5137.        MOV CX,BX
  5138.        REP MOVSB   {8.row  }
  5139.        ADD DI,AX
  5140.        ADD SI,DX
  5141.        MOV CX,BX
  5142.        REP MOVSB   {9.row  }
  5143.        ADD DI,AX
  5144.        ADD SI,DX
  5145.        MOV CX,BX
  5146.        REP MOVSB   {10.row  }
  5147.        ADD DI,AX
  5148.        ADD SI,DX
  5149.        MOV CX,BX
  5150.        REP MOVSB   {11.row  }
  5151.        ADD DI,AX
  5152.        ADD SI,DX
  5153.        MOV CX,BX
  5154.        REP MOVSB   {12.row  }
  5155.        ADD DI,AX
  5156.        ADD SI,DX
  5157.        MOV CX,BX
  5158.        REP MOVSB   {13.row  }
  5159.        ADD DI,AX
  5160.        ADD SI,DX
  5161.        MOV CX,BX
  5162.        REP MOVSB   {14.row  }
  5163.        ADD DI,AX
  5164.        ADD SI,DX
  5165.        MOV CX,BX
  5166.        REP MOVSB   {15.row  }
  5167.        ADD DI,AX
  5168.        ADD SI,DX
  5169.        MOV CX,BX
  5170.        REP MOVSB   {16.row  }
  5171.        ADD DI,AX
  5172.        ADD SI,DX
  5173.  
  5174.  
  5175.        MOV DX,3C4h
  5176.        MOV AX,0402h  {choose write plane 2  }
  5177.        OUT DX,AX
  5178.  
  5179.        MOV DX,3CEh   {next read plane:   }
  5180.        POP AX
  5181.        INC AH
  5182.        AND AH,3      {increment by 1 MOD 4}
  5183.        JNE @nowrap2h
  5184.        INC SI        {plane 0 follows after plane 3 again, but}
  5185.                      {the source address has increased by 1 byte }
  5186.      @nowrap2h:
  5187.        OUT DX,AX
  5188.        PUSH AX
  5189.        SUB DI,16*LINESIZE   {reset DI and SI registers     }
  5190.        SUB SI,16*4
  5191.  
  5192.  
  5193.        DEC BP        {BP:=16+1-leftcut}
  5194.        MOV BX,BP
  5195.        SHR BX,1
  5196.        SHR BX,1      {BX:=bytes_per_plane2_row }
  5197.  
  5198.        MOV AX,LINESIZE {adjustment for row target addresses    }
  5199.        SUB AX,BX       { := LINESIZE-bytes_per_plane2_row }
  5200.        MOV DX,4        {adjustment for source addresses of the tiles}
  5201.        SUB DX,BX       { := 4-bytes_per_plane2_row }
  5202.  
  5203.        MOV CX,BX   {CX:=bytes_per_plane2_row }
  5204.        REP MOVSB   {1.row  }
  5205.        ADD DI,AX   {set DI to next destination row  }
  5206.        ADD SI,DX   {set SI to next tile's source addr.   }
  5207.        MOV CX,BX
  5208.        REP MOVSB   {2.row  }
  5209.        ADD DI,AX
  5210.        ADD SI,DX
  5211.        MOV CX,BX
  5212.        REP MOVSB   {3.row  }
  5213.        ADD DI,AX
  5214.        ADD SI,DX
  5215.        MOV CX,BX
  5216.        REP MOVSB   {4.row  }
  5217.        ADD DI,AX
  5218.        ADD SI,DX
  5219.        MOV CX,BX
  5220.        REP MOVSB   {5.row  }
  5221.        ADD DI,AX
  5222.        ADD SI,DX
  5223.        MOV CX,BX
  5224.        REP MOVSB   {6.row  }
  5225.        ADD DI,AX
  5226.        ADD SI,DX
  5227.        MOV CX,BX
  5228.        REP MOVSB   {7.row  }
  5229.        ADD DI,AX
  5230.        ADD SI,DX
  5231.        MOV CX,BX
  5232.        REP MOVSB   {8.row  }
  5233.        ADD DI,AX
  5234.        ADD SI,DX
  5235.        MOV CX,BX
  5236.        REP MOVSB   {9.row  }
  5237.        ADD DI,AX
  5238.        ADD SI,DX
  5239.        MOV CX,BX
  5240.        REP MOVSB   {10.row  }
  5241.        ADD DI,AX
  5242.        ADD SI,DX
  5243.        MOV CX,BX
  5244.        REP MOVSB   {11.row  }
  5245.        ADD DI,AX
  5246.        ADD SI,DX
  5247.        MOV CX,BX
  5248.        REP MOVSB   {12.row  }
  5249.        ADD DI,AX
  5250.        ADD SI,DX
  5251.        MOV CX,BX
  5252.        REP MOVSB   {13.row  }
  5253.        ADD DI,AX
  5254.        ADD SI,DX
  5255.        MOV CX,BX
  5256.        REP MOVSB   {14.row  }
  5257.        ADD DI,AX
  5258.        ADD SI,DX
  5259.        MOV CX,BX
  5260.        REP MOVSB   {15.row  }
  5261.        ADD DI,AX
  5262.        ADD SI,DX
  5263.        MOV CX,BX
  5264.        REP MOVSB   {16.row  }
  5265.        ADD DI,AX
  5266.        ADD SI,DX
  5267.  
  5268.  
  5269.        MOV DX,3C4h
  5270.        MOV AX,0802h  {choose write plane 3  }
  5271.        OUT DX,AX
  5272.  
  5273.        MOV DX,3CEh   {next read plane:   }
  5274.        POP AX
  5275.        INC AH
  5276.        AND AH,3      {increment by 1 MOD 4}
  5277.        JNE @nowrap3h
  5278.        INC SI        {plane 0 follows after plane 3 again, but}
  5279.                      {the source address has increased by 1 byte }
  5280.      @nowrap3h:
  5281.        OUT DX,AX
  5282.                      {do not push value again!}
  5283.        SUB DI,16*LINESIZE   {reset DI and SI registers     }
  5284.        SUB SI,16*4
  5285.  
  5286.  
  5287.  
  5288.        DEC BP        {BP:=16-leftcut}
  5289.      @lastplane8:
  5290.                      
  5291.        SHR BP,1      {use BP directly    }
  5292.        SHR BP,1      {BP:=bytes_per_plane3_row }
  5293.  
  5294.        MOV AX,LINESIZE {adjustment for row target addresses    }
  5295.        SUB AX,BP       { := LINESIZE-bytes_per_plane3_row }
  5296.        MOV DX,4        {adjustment for source addresses of the tiles}
  5297.        SUB DX,BP       { := 4-bytes_per_plane3_row }
  5298.  
  5299.        MOV CX,BP   {CX:=bytes_per_plane2_row }
  5300.        REP MOVSB   {1.row  }
  5301.        ADD DI,AX   {set DI to next destination row  }
  5302.        ADD SI,DX   {set SI to next tile's source addr.   }
  5303.        MOV CX,BP
  5304.        REP MOVSB   {2.row  }
  5305.        ADD DI,AX
  5306.        ADD SI,DX
  5307.        MOV CX,BP
  5308.        REP MOVSB   {3.row  }
  5309.        ADD DI,AX
  5310.        ADD SI,DX
  5311.        MOV CX,BP
  5312.        REP MOVSB   {4.row  }
  5313.        ADD DI,AX
  5314.        ADD SI,DX
  5315.        MOV CX,BP
  5316.        REP MOVSB   {5.row  }
  5317.        ADD DI,AX
  5318.        ADD SI,DX
  5319.        MOV CX,BP
  5320.        REP MOVSB   {6.row  }
  5321.        ADD DI,AX
  5322.        ADD SI,DX
  5323.        MOV CX,BP
  5324.        REP MOVSB   {7.row  }
  5325.        ADD DI,AX
  5326.        ADD SI,DX
  5327.        MOV CX,BP
  5328.        REP MOVSB   {8.row  }
  5329.        ADD DI,AX
  5330.        ADD SI,DX
  5331.        MOV CX,BP
  5332.        REP MOVSB   {9.row  }
  5333.        ADD DI,AX
  5334.        ADD SI,DX
  5335.        MOV CX,BP
  5336.        REP MOVSB   {10.row  }
  5337.        ADD DI,AX
  5338.        ADD SI,DX
  5339.        MOV CX,BP
  5340.        REP MOVSB   {11.row  }
  5341.        ADD DI,AX
  5342.        ADD SI,DX
  5343.        MOV CX,BP
  5344.        REP MOVSB   {12.row  }
  5345.        ADD DI,AX
  5346.        ADD SI,DX
  5347.        MOV CX,BP
  5348.        REP MOVSB   {13.row  }
  5349.        ADD DI,AX
  5350.        ADD SI,DX
  5351.        MOV CX,BP
  5352.        REP MOVSB   {14.row  }
  5353.        ADD DI,AX
  5354.        ADD SI,DX
  5355.        MOV CX,BP
  5356.        REP MOVSB   {15.row  }
  5357.        ADD DI,AX
  5358.        ADD SI,DX
  5359.        MOV CX,BP
  5360.        REP MOVSB   {16.row  }
  5361.  
  5362.      @LeftTileDone:
  5363.        POP BP
  5364.        MOV AX,SEG @Data  {restore DS         }
  5365.        MOV DS,AX
  5366.  
  5367.     MOV CL,offscreenFlag
  5368.     XOR SI,SI
  5369.     DEC CL
  5370.     JZ @go12
  5371.     MOV AX,xt
  5372.     MOV DX,tiles
  5373.     ADD AX,DX
  5374.     JS @go12
  5375.     CMP AX,XTiles
  5376.     JAE @go12
  5377.     MOV SI,RandIndex
  5378.     ADD SI,DX
  5379.   @go12:
  5380.  
  5381.  
  5382.        {PROCEDURE DrawRightTile(x,y,rightcut:INTEGER; index:WORD);}
  5383.        { in: (x,y) = upper left corner of the tile to be drawn}
  5384.        {     rightcut = number of right tile-columns to be cut off     }
  5385.        {     SI = index = tile number}
  5386.        {out: tile has been drawn on actual page PAGEADR       }
  5387.        {rem: tile has been clipped at its right accordingly}
  5388.        {     rightcut must lie between 0..15, 16 is allowed (but senseless)!        }
  5389.        {     rightcut could also be computed (for x>xmax-16) by             }
  5390.        {     rightcut := x+15-xmax }
  5391.        MOV AH,[OFFSET BackTile +SI]  {AH:=BackTile[index]}
  5392.        XOR AL,AL
  5393.  
  5394.        SHR AH,1    {AX:=tile*64 =tile SHL 6 =(tile SHL 8) SHR 2      }
  5395.        RCR AL,1    {therefore: set AH:=tile and afterwards, shift AX }
  5396.        SHR AH,1    {2 bits to the right!}
  5397.        RCR AL,1
  5398.        MOV SI,AX   {That's also the offset part of the source ad. in page3}
  5399.  
  5400.        MOV DI,y    {the first destination addr. is DI:=y*LINESIZE+(x div 4)}
  5401.        SHL DI,1
  5402.        MOV DI,CS:[OFFSET GADR + DI]
  5403.        MOV AX,x
  5404.        MOV BX,AX   {place a copy of x in BX}
  5405.        SHR AX,1
  5406.        SHR AX,1
  5407.        ADD DI,AX
  5408.  
  5409.        MOV CX,rightcut
  5410.  
  5411.        {Now there is no further variable on stack, so BP can be used   }
  5412.        {for other purposes!}
  5413.        PUSH BP     {will be needed when leaving the procedure! }
  5414.        MOV BP,16+3
  5415.        SUB BP,CX   {BP:=16+3-rightcut, because the number of bytes per row  }
  5416.                    {of plane i is computed by (16+3-i-rightcut) SHR 2 }
  5417.  
  5418.  
  5419.        MOV ES,PAGEADR    {(segment part of) dest. address is active page}
  5420.        MOV DS,SCROLLADR  {(segment part of) source addr. is page SCROLLPAGE}
  5421.  
  5422.        MOV DX,3C4h
  5423.        MOV AL,2
  5424.        AND BX,3
  5425.  
  5426.        JNE @mode0i {only if x mod 4=0 _and_ rightcut mod 4=0, then we can }
  5427.        AND CX,3    {use write mode 1!        }
  5428.        JNE @mode0i
  5429.  
  5430.        {--- short-cut possible, using write mode1: DX=3C4h, BP=16+3-rightcut   }
  5431.        MOV AH,0Fh
  5432.        OUT DX,AX    {work on all 4 planes simultaneously  }
  5433.        MOV DX,3CEh
  5434.        MOV AX,4105h {choose write mode 1}
  5435.        OUT DX,AX
  5436.        SUB BP,3     {set BP to proper size              }
  5437.  
  5438.        SHR BP,1      {use BP directly    }
  5439.        SHR BP,1      {BP:=bytes_per_plane3_row }
  5440.  
  5441.        MOV AX,LINESIZE {adjustment for row target addresses    }
  5442.        SUB AX,BP       { := LINESIZE-bytes_per_plane3_row }
  5443.        MOV DX,4        {adjustment for source addresses of the tiles}
  5444.        SUB DX,BP       { := 4-bytes_per_plane3_row }
  5445.  
  5446.        MOV CX,BP   {CX:=bytes_per_plane2_row }
  5447.        REP MOVSB   {1.row  }
  5448.        ADD DI,AX   {set DI to next destination row  }
  5449.        ADD SI,DX   {set SI to next tile's source addr.   }
  5450.        MOV CX,BP
  5451.        REP MOVSB   {2.row  }
  5452.        ADD DI,AX
  5453.        ADD SI,DX
  5454.        MOV CX,BP
  5455.        REP MOVSB   {3.row  }
  5456.        ADD DI,AX
  5457.        ADD SI,DX
  5458.        MOV CX,BP
  5459.        REP MOVSB   {4.row  }
  5460.        ADD DI,AX
  5461.        ADD SI,DX
  5462.        MOV CX,BP
  5463.        REP MOVSB   {5.row  }
  5464.        ADD DI,AX
  5465.        ADD SI,DX
  5466.        MOV CX,BP
  5467.        REP MOVSB   {6.row  }
  5468.        ADD DI,AX
  5469.        ADD SI,DX
  5470.        MOV CX,BP
  5471.        REP MOVSB   {7.row  }
  5472.        ADD DI,AX
  5473.        ADD SI,DX
  5474.        MOV CX,BP
  5475.        REP MOVSB   {8.row  }
  5476.        ADD DI,AX
  5477.        ADD SI,DX
  5478.        MOV CX,BP
  5479.        REP MOVSB   {9.row  }
  5480.        ADD DI,AX
  5481.        ADD SI,DX
  5482.        MOV CX,BP
  5483.        REP MOVSB   {10.row  }
  5484.        ADD DI,AX
  5485.        ADD SI,DX
  5486.        MOV CX,BP
  5487.        REP MOVSB   {11.row  }
  5488.        ADD DI,AX
  5489.        ADD SI,DX
  5490.        MOV CX,BP
  5491.        REP MOVSB   {12.row  }
  5492.        ADD DI,AX
  5493.        ADD SI,DX
  5494.        MOV CX,BP
  5495.        REP MOVSB   {13.row  }
  5496.        ADD DI,AX
  5497.        ADD SI,DX
  5498.        MOV CX,BP
  5499.        REP MOVSB   {14.row  }
  5500.        ADD DI,AX
  5501.        ADD SI,DX
  5502.        MOV CX,BP
  5503.        REP MOVSB   {15.row  }
  5504.        ADD DI,AX
  5505.        ADD SI,DX
  5506.        MOV CX,BP
  5507.        REP MOVSB   {16.row  }
  5508.  
  5509.        {--- short cut has been taken: thus, reset write mode 0!     }
  5510.        MOV AX,4005h
  5511.        MOV DX,3CEh
  5512.        OUT DX,AX
  5513.        {---}
  5514.        JMP @RightTileDone
  5515.  
  5516.      @mode0i:
  5517.        MOV AH,CS:[OFFSET CS_TranslateTab +BX]
  5518.        OUT DX,AX   {choose plane to write at     }
  5519.        PUSH AX     {and preserve it for later}
  5520.  
  5521.        MOV DX,3CEh
  5522.        MOV AX,0004h    {choose read plane 0}
  5523.        OUT DX,AX
  5524.  
  5525.        MOV BX,BP
  5526.        SHR BX,1
  5527.        SHR BX,1    {BX:=bytes_per_plane0_row = (16+3-rightcut) DIV 4 }
  5528.  
  5529.        MOV AX,LINESIZE {adjustment for row target addresses    }
  5530.        SUB AX,BX       { := LINESIZE-bytes_per_plane0_row }
  5531.        MOV DX,4        {adjustment for source addresses of the tiles}
  5532.        SUB DX,BX       { := 4-bytes_per_plane0_row }
  5533.  
  5534.        MOV CX,BX   {CX:=bytes_per_plane0_row }
  5535.        REP MOVSB   {1.row  }
  5536.        ADD DI,AX   {set DI to next destination row  }
  5537.        ADD SI,DX   {set SI to next tile's source addr.   }
  5538.        MOV CX,BX
  5539.        REP MOVSB   {2.row  }
  5540.        ADD DI,AX
  5541.        ADD SI,DX
  5542.        MOV CX,BX
  5543.        REP MOVSB   {3.row  }
  5544.        ADD DI,AX
  5545.        ADD SI,DX
  5546.        MOV CX,BX
  5547.        REP MOVSB   {4.row  }
  5548.        ADD DI,AX
  5549.        ADD SI,DX
  5550.        MOV CX,BX
  5551.        REP MOVSB   {5.row  }
  5552.        ADD DI,AX
  5553.        ADD SI,DX
  5554.        MOV CX,BX
  5555.        REP MOVSB   {6.row  }
  5556.        ADD DI,AX
  5557.        ADD SI,DX
  5558.        MOV CX,BX
  5559.        REP MOVSB   {7.row  }
  5560.        ADD DI,AX
  5561.        ADD SI,DX
  5562.        MOV CX,BX
  5563.        REP MOVSB   {8.row  }
  5564.        ADD DI,AX
  5565.        ADD SI,DX
  5566.        MOV CX,BX
  5567.        REP MOVSB   {9.row  }
  5568.        ADD DI,AX
  5569.        ADD SI,DX
  5570.        MOV CX,BX
  5571.        REP MOVSB   {10.row  }
  5572.        ADD DI,AX
  5573.        ADD SI,DX
  5574.        MOV CX,BX
  5575.        REP MOVSB   {11.row  }
  5576.        ADD DI,AX
  5577.        ADD SI,DX
  5578.        MOV CX,BX
  5579.        REP MOVSB   {12.row  }
  5580.        ADD DI,AX
  5581.        ADD SI,DX
  5582.        MOV CX,BX
  5583.        REP MOVSB   {13.row  }
  5584.        ADD DI,AX
  5585.        ADD SI,DX
  5586.        MOV CX,BX
  5587.        REP MOVSB   {14.row  }
  5588.        ADD DI,AX
  5589.        ADD SI,DX
  5590.        MOV CX,BX
  5591.        REP MOVSB   {15.row  }
  5592.        ADD DI,AX
  5593.        ADD SI,DX
  5594.        MOV CX,BX
  5595.        REP MOVSB   {16.row  }
  5596.        ADD DI,AX
  5597.        ADD SI,DX
  5598.  
  5599.  
  5600.        MOV DX,3CEh
  5601.        MOV AX,0104h  {choose read plane 1}
  5602.        OUT DX,AX
  5603.  
  5604.        MOV DX,3C4h   {next write plane:     }
  5605.        POP AX
  5606.        SHL AH,1
  5607.        CMP AH,16
  5608.        JNE @nowrap1i
  5609.        MOV AH,1      {plane 0 follows after plane 3 again, but}
  5610.        INC DI        {the destination address has increased by 1 byte}
  5611.      @nowrap1i:
  5612.        OUT DX,AX
  5613.        PUSH AX
  5614.        SUB DI,16*LINESIZE   {reset DI and SI registers     }
  5615.        SUB SI,16*4
  5616.  
  5617.  
  5618.        DEC BP        {BP:=16+2-rightcut}
  5619.        MOV BX,BP
  5620.        SHR BX,1
  5621.        SHR BX,1      {BX:=bytes_per_plane1_row }
  5622.  
  5623.        MOV AX,LINESIZE {adjustment for row target addresses    }
  5624.        SUB AX,BX       { := LINESIZE-bytes_per_plane1_row }
  5625.        MOV DX,4        {adjustment for source addresses of the tiles}
  5626.        SUB DX,BX       { := 4-bytes_per_plane1_row }
  5627.  
  5628.        MOV CX,BX   {CX:=bytes_per_plane1_row }
  5629.        REP MOVSB   {1.row  }
  5630.        ADD DI,AX   {set DI to next destination row  }
  5631.        ADD SI,DX   {set SI to next tile's source addr.   }
  5632.        MOV CX,BX
  5633.        REP MOVSB   {2.row  }
  5634.        ADD DI,AX
  5635.        ADD SI,DX
  5636.        MOV CX,BX
  5637.        REP MOVSB   {3.row  }
  5638.        ADD DI,AX
  5639.        ADD SI,DX
  5640.        MOV CX,BX
  5641.        REP MOVSB   {4.row  }
  5642.        ADD DI,AX
  5643.        ADD SI,DX
  5644.        MOV CX,BX
  5645.        REP MOVSB   {5.row  }
  5646.        ADD DI,AX
  5647.        ADD SI,DX
  5648.        MOV CX,BX
  5649.        REP MOVSB   {6.row  }
  5650.        ADD DI,AX
  5651.        ADD SI,DX
  5652.        MOV CX,BX
  5653.        REP MOVSB   {7.row  }
  5654.        ADD DI,AX
  5655.        ADD SI,DX
  5656.        MOV CX,BX
  5657.        REP MOVSB   {8.row  }
  5658.        ADD DI,AX
  5659.        ADD SI,DX
  5660.        MOV CX,BX
  5661.        REP MOVSB   {9.row  }
  5662.        ADD DI,AX
  5663.        ADD SI,DX
  5664.        MOV CX,BX
  5665.        REP MOVSB   {10.row  }
  5666.        ADD DI,AX
  5667.        ADD SI,DX
  5668.        MOV CX,BX
  5669.        REP MOVSB   {11.row  }
  5670.        ADD DI,AX
  5671.        ADD SI,DX
  5672.        MOV CX,BX
  5673.        REP MOVSB   {12.row  }
  5674.        ADD DI,AX
  5675.        ADD SI,DX
  5676.        MOV CX,BX
  5677.        REP MOVSB   {13.row  }
  5678.        ADD DI,AX
  5679.        ADD SI,DX
  5680.        MOV CX,BX
  5681.        REP MOVSB   {14.row  }
  5682.        ADD DI,AX
  5683.        ADD SI,DX
  5684.        MOV CX,BX
  5685.        REP MOVSB   {15.row  }
  5686.        ADD DI,AX
  5687.        ADD SI,DX
  5688.        MOV CX,BX
  5689.        REP MOVSB   {16.row  }
  5690.        ADD DI,AX
  5691.        ADD SI,DX
  5692.  
  5693.  
  5694.        MOV DX,3CEh
  5695.        MOV AX,0204h  {choose read plane 2}
  5696.        OUT DX,AX
  5697.  
  5698.        MOV DX,3C4h   {next write plane:     }
  5699.        POP AX
  5700.        SHL AH,1
  5701.        CMP AH,16
  5702.        JNE @nowrap2i
  5703.        MOV AH,1      {plane 0 follows after plane 3 again, but}
  5704.        INC DI        {the destination address has increased by 1 byte}
  5705.      @nowrap2i:
  5706.        OUT DX,AX
  5707.        PUSH AX
  5708.        SUB DI,16*LINESIZE   {reset DI and SI registers     }
  5709.        SUB SI,16*4
  5710.  
  5711.  
  5712.        DEC BP        {BP:=16+1-rightcut}
  5713.        MOV BX,BP
  5714.        SHR BX,1
  5715.        SHR BX,1      {BX:=bytes_per_plane2_row }
  5716.  
  5717.        MOV AX,LINESIZE {adjustment for row target addresses    }
  5718.        SUB AX,BX       { := LINESIZE-bytes_per_plane2_row }
  5719.        MOV DX,4        {adjustment for source addresses of the tiles}
  5720.        SUB DX,BX       { := 4-bytes_per_plane2_row }
  5721.  
  5722.        MOV CX,BX   {CX:=bytes_per_plane2_row }
  5723.        REP MOVSB   {1.row  }
  5724.        ADD DI,AX   {set DI to next destination row  }
  5725.        ADD SI,DX   {set SI to next tile's source addr.   }
  5726.        MOV CX,BX
  5727.        REP MOVSB   {2.row  }
  5728.        ADD DI,AX
  5729.        ADD SI,DX
  5730.        MOV CX,BX
  5731.        REP MOVSB   {3.row  }
  5732.        ADD DI,AX
  5733.        ADD SI,DX
  5734.        MOV CX,BX
  5735.        REP MOVSB   {4.row  }
  5736.        ADD DI,AX
  5737.        ADD SI,DX
  5738.        MOV CX,BX
  5739.        REP MOVSB   {5.row  }
  5740.        ADD DI,AX
  5741.        ADD SI,DX
  5742.        MOV CX,BX
  5743.        REP MOVSB   {6.row  }
  5744.        ADD DI,AX
  5745.        ADD SI,DX
  5746.        MOV CX,BX
  5747.        REP MOVSB   {7.row  }
  5748.        ADD DI,AX
  5749.        ADD SI,DX
  5750.        MOV CX,BX
  5751.        REP MOVSB   {8.row  }
  5752.        ADD DI,AX
  5753.        ADD SI,DX
  5754.        MOV CX,BX
  5755.        REP MOVSB   {9.row  }
  5756.        ADD DI,AX
  5757.        ADD SI,DX
  5758.        MOV CX,BX
  5759.        REP MOVSB   {10.row  }
  5760.        ADD DI,AX
  5761.        ADD SI,DX
  5762.        MOV CX,BX
  5763.        REP MOVSB   {11.row  }
  5764.        ADD DI,AX
  5765.        ADD SI,DX
  5766.        MOV CX,BX
  5767.        REP MOVSB   {12.row  }
  5768.        ADD DI,AX
  5769.        ADD SI,DX
  5770.        MOV CX,BX
  5771.        REP MOVSB   {13.row  }
  5772.        ADD DI,AX
  5773.        ADD SI,DX
  5774.        MOV CX,BX
  5775.        REP MOVSB   {14.row  }
  5776.        ADD DI,AX
  5777.        ADD SI,DX
  5778.        MOV CX,BX
  5779.        REP MOVSB   {15.row  }
  5780.        ADD DI,AX
  5781.        ADD SI,DX
  5782.        MOV CX,BX
  5783.        REP MOVSB   {16.row  }
  5784.        ADD DI,AX
  5785.        ADD SI,DX
  5786.  
  5787.  
  5788.        MOV DX,3CEh
  5789.        MOV AX,0304h  {choose read plane 3}
  5790.        OUT DX,AX
  5791.  
  5792.        MOV DX,3C4h   {next write plane:     }
  5793.        POP AX
  5794.        SHL AH,1
  5795.        CMP AH,16
  5796.        JNE @nowrap3i
  5797.        MOV AH,1      {plane 0 follows after plane 3 again, but}
  5798.        INC DI        {the destination address has increased by 1 byte}
  5799.      @nowrap3i:
  5800.        OUT DX,AX
  5801.                      {do not push value again!}
  5802.        SUB DI,16*LINESIZE   {reset DI and SI registers     }
  5803.        SUB SI,16*4
  5804.  
  5805.  
  5806.  
  5807.        DEC BP        {BP:=16-rightcut}
  5808.      @lastplane9:
  5809.                      
  5810.        SHR BP,1      {use BP directly    }
  5811.        SHR BP,1      {BP:=bytes_per_plane3_row }
  5812.  
  5813.        MOV AX,LINESIZE {adjustment for row target addresses    }
  5814.        SUB AX,BP       { := LINESIZE-bytes_per_plane3_row }
  5815.        MOV DX,4        {adjustment for source addresses of the tiles}
  5816.        SUB DX,BP       { := 4-bytes_per_plane3_row }
  5817.  
  5818.        MOV CX,BP   {CX:=bytes_per_plane2_row }
  5819.        REP MOVSB   {1.row  }
  5820.        ADD DI,AX   {set DI to next destination row  }
  5821.        ADD SI,DX   {set SI to next tile's source addr.   }
  5822.        MOV CX,BP
  5823.        REP MOVSB   {2.row  }
  5824.        ADD DI,AX
  5825.        ADD SI,DX
  5826.        MOV CX,BP
  5827.        REP MOVSB   {3.row  }
  5828.        ADD DI,AX
  5829.        ADD SI,DX
  5830.        MOV CX,BP
  5831.        REP MOVSB   {4.row  }
  5832.        ADD DI,AX
  5833.        ADD SI,DX
  5834.        MOV CX,BP
  5835.        REP MOVSB   {5.row  }
  5836.        ADD DI,AX
  5837.        ADD SI,DX
  5838.        MOV CX,BP
  5839.        REP MOVSB   {6.row  }
  5840.        ADD DI,AX
  5841.        ADD SI,DX
  5842.        MOV CX,BP
  5843.        REP MOVSB   {7.row  }
  5844.        ADD DI,AX
  5845.        ADD SI,DX
  5846.        MOV CX,BP
  5847.        REP MOVSB   {8.row  }
  5848.        ADD DI,AX
  5849.        ADD SI,DX
  5850.        MOV CX,BP
  5851.        REP MOVSB   {9.row  }
  5852.        ADD DI,AX
  5853.        ADD SI,DX
  5854.        MOV CX,BP
  5855.        REP MOVSB   {10.row  }
  5856.        ADD DI,AX
  5857.        ADD SI,DX
  5858.        MOV CX,BP
  5859.        REP MOVSB   {11.row  }
  5860.        ADD DI,AX
  5861.        ADD SI,DX
  5862.        MOV CX,BP
  5863.        REP MOVSB   {12.row  }
  5864.        ADD DI,AX
  5865.        ADD SI,DX
  5866.        MOV CX,BP
  5867.        REP MOVSB   {13.row  }
  5868.        ADD DI,AX
  5869.        ADD SI,DX
  5870.        MOV CX,BP
  5871.        REP MOVSB   {14.row  }
  5872.        ADD DI,AX
  5873.        ADD SI,DX
  5874.        MOV CX,BP
  5875.        REP MOVSB   {15.row  }
  5876.        ADD DI,AX
  5877.        ADD SI,DX
  5878.        MOV CX,BP
  5879.        REP MOVSB   {16.row  }
  5880.  
  5881.      @RightTileDone:
  5882.        POP BP
  5883.        MOV AX,SEG @Data  {restore DS         }
  5884.        MOV DS,AX
  5885.  
  5886.     MOV AX,RandIndex
  5887.     ADD AX,XTiles
  5888.     MOV RandIndex,AX
  5889.     MOV AX,yt
  5890.     INC AX
  5891.     MOV yt,AX
  5892.     MOV DX,y
  5893.     ADD DX,16
  5894.     MOV y,DX
  5895.     CMP DX,YMAX+1-16
  5896.     JBE @repeat5
  5897.   @ende:
  5898.  
  5899.  
  5900.   {------- starting here: put sprites on actual graphic page}
  5901.   @Sprites_zeichnen:
  5902.     MOV SI,NMAX*2
  5903.     PUSH BP      {pop BP in the end!       }
  5904.  
  5905.     @zeichne:
  5906.  
  5907.     {DS = normal data segment, ES = graphic page segment,  }
  5908.     {SI = spritepositionnumber*2                           }
  5909.     @SZeich:
  5910.       MOV BX,[SI + OFFSET SpriteN]   {BX=SpriteN[?]=Spriteloadnumber}
  5911.       SHL BX,1                       {BX = sprite load number*2}
  5912.  
  5913.     {Now: compute "SpriteN[?]:=SpriteN[NextSprite[?]]"     }
  5914.       MOV BX,[BX + OFFSET NextSprite] {AX=NextSprite[SpriteN[?]]}
  5915.       MOV [SI + OFFSET SpriteN],BX    {use it as new SpriteN[?] value }
  5916.       SHL BX,1
  5917.  
  5918.  
  5919.       JNZ @aktiv
  5920.       JMP @noSprite
  5921.  
  5922.  
  5923.     @aktiv:
  5924.       PUSH SI           {save spritepositionnumber*2   }
  5925.  
  5926.       MOV DX,[SI + OFFSET SpriteX]   {if SpriteX>xmax then skip_sprite }
  5927.       SUB DX,StartVirtualX           {virtual -> absolute coordinates  }
  5928.       CMP DX,XMAX
  5929.       JLE @L0
  5930.     @ToSprite_fertig:                {jump-rail to @Sprite_fertig   }
  5931.       JMP @Sprite_fertig
  5932.     @L0:
  5933.       MOV CS:WORD PTR @akt_SpriteX+1,DX
  5934.       MOV DI,[SI + OFFSET SpriteY]   {DI = SpriteY_virtual }
  5935.       SUB DI,StartVirtualY           {DI = SpriteY (absolute!)}
  5936.       MOV DS,[BX + OFFSET SPRITEAD]  {!!!DS = ^sprite data !!!}
  5937.       MOV AX,[Breite]                {AX = width in groups of 4 }
  5938.       MOV CS:WORD PTR @max_Breite+1,AX
  5939.       MOV SI,AX                      {SI = dto.}
  5940.       SHL AX,1
  5941.       SHL AX,1                       {AX = max_width_in_points  }
  5942.       ADD AX,DX                      {AX = max_width_in_points+SpriteX  }
  5943.       JS @ToSprite_fertig
  5944.       MOV BX,DI                      {if SpriteY>=0 then starty:=+SpriteY}
  5945.       NEG DI                         {              else starty:=-SpriteY}
  5946.       MOV BP,DI
  5947.       JG @Top_cut
  5948.       XOR DI,DI
  5949.     @Top_cut:                        {DI = starty, BP = -SpriteY}
  5950.       MOV AX,[Hoehe]                 {AX = height (in rows)   }
  5951.       CMP DI,AX                      {if starty>=height then skip_sprite}
  5952.       JGE @ToSprite_fertig
  5953.       ADD BP,YMAX                    {BP = -SpriteY+ymax}
  5954.       JL @ToSprite_fertig            {(a bit lax:)  }
  5955.       CMP AX,BP                      {if height+SpriteY>ymax   }
  5956.       JG @To_then                    { then [ endy:=199-SpriteY }
  5957.       DEC AX                         {       if endy<0 then skip_sprite ] }
  5958.       MOV BP,AX                      { else endy:=height-1 }
  5959.  
  5960.     {BP = endy, SI=[@max_Breite+1] = max_width_in_groups of 4,  }
  5961.     {DI = starty, BX = SpriteY, DX=[@akt_SpriteX+1] = SpriteX,  }
  5962.     {DS = ^sprite data, ES = ^graphic page}
  5963.     @To_then:
  5964.       MOV AX,BP
  5965.       SUB BP,DI
  5966.  
  5967.       SHL BP,1
  5968.       MOV [End_min_Start],BP         {= (endy-starty)*2 =Yaktuell*2}
  5969.       ADD BX,AX
  5970.       SHL BX,1
  5971.       MOV BX,CS:[OFFSET gadr + BX]   {BX=zeilenadr:=(endy+SpriteY)*LINESIZE}
  5972.       MOV [zeilenadr],BX             {store it in [zeilenadr], too }
  5973.       MOV BP,DX
  5974.       MUL SI                        {AX=endy*max_width_in_groups_of_4=yoffset}
  5975.       MOV [yoffset_],AX              {store it in [yoffset_], too}
  5976.       SHL DI,1                       {DI = starty*2}
  5977.       MOV CS:WORD PTR @Starty_2+1,DI {store it in [@Starty_2+1], too }
  5978.  
  5979.       {Now: look at sprite's mode byte and determine if an other than the }
  5980.       {momentary active sprite display method is needed; if so, then copy }
  5981.       {in the right one!                                                  }
  5982.       {used registers: AX and SI                                          }
  5983.        MOV AL,[Modus]                {get mode byte of the sprite}
  5984.        XOR AH,AH
  5985.        SHL AX,1
  5986.        MOV SI,AX
  5987.        MOV SI,CS:[OFFSET Adressen +SI] {get pointer to according routine     }
  5988.        MOV AX,CS:[SI]
  5989.        CMP AX,CS:[WORD PTR @Patch1]    {is this routine already active? }
  5990.        JE @no_newcode                  {yes, nothing to do}
  5991.        PUSH DS                         {no, copy routine to all places  }
  5992.        PUSH CS                         {where it will be needed}
  5993.        POP DS
  5994.        MOV [WORD PTR @Patch1],AX
  5995.        MOV [WORD PTR @Patch2],AX
  5996.        MOV [WORD PTR @Patch3],AX
  5997.        MOV [WORD PTR @Patch4],AX
  5998.        INC SI
  5999.        INC SI
  6000.        LODSW
  6001.        MOV [WORD PTR @Patch1+2],AX
  6002.        MOV [WORD PTR @Patch2+2],AX
  6003.        MOV [WORD PTR @Patch3+2],AX
  6004.        MOV [WORD PTR @Patch4+2],AX
  6005.        LODSW
  6006.        MOV [WORD PTR @Patch1+4],AX
  6007.        MOV [WORD PTR @Patch2+4],AX
  6008.        MOV [WORD PTR @Patch3+4],AX
  6009.        MOV [WORD PTR @Patch4+4],AX
  6010.        LODSW
  6011.        MOV [WORD PTR @Patch1+6],AX
  6012.        MOV [WORD PTR @Patch2+6],AX
  6013.        MOV [WORD PTR @Patch3+6],AX
  6014.        MOV [WORD PTR @Patch4+6],AX
  6015.        LODSW
  6016.        MOV [WORD PTR @Patch1+8],AX
  6017.        MOV [WORD PTR @Patch2+8],AX
  6018.        MOV [WORD PTR @Patch3+8],AX
  6019.        MOV [WORD PTR @Patch4+8],AX
  6020.        LODSW
  6021.        MOV [WORD PTR @Patch1+10],AX
  6022.        MOV [WORD PTR @Patch2+10],AX
  6023.        MOV [WORD PTR @Patch3+10],AX
  6024.        MOV [WORD PTR @Patch4+10],AX
  6025.        LODSW
  6026.        MOV [WORD PTR @Patch1+12],AX
  6027.        MOV [WORD PTR @Patch2+12],AX
  6028.        MOV [WORD PTR @Patch3+12],AX
  6029.        MOV [WORD PTR @Patch4+12],AX
  6030.        LODSW
  6031.        MOV [WORD PTR @Patch1+14],AX
  6032.        MOV [WORD PTR @Patch2+14],AX
  6033.        MOV [WORD PTR @Patch3+14],AX
  6034.        MOV [WORD PTR @Patch4+14],AX
  6035.  
  6036.        POP DS                          {restore DS         }
  6037.      @no_newcode:
  6038.  
  6039.  
  6040.     {(AX=)[yoffset_]     = yoffset }
  6041.     { BX=[zeilenadr]     = (endy+SpriteY)*LINESIZE}
  6042.     { DI=[@Starty_2+1]   = starty*2}
  6043.     {(SI=[@max_Breite+1] = max_width_in_groups_of_4) }
  6044.     { BP                 = SpriteX}
  6045.     { DS                 = ^sprite data}
  6046.     { ES                 = ^graphic page}
  6047.     { [end_min_start]    = (endy-starty)*2 =Yaktuell*2}
  6048.     { [@max_Breite+1]    = max_width_in_groups_of_4   }
  6049.     { [@akt_SpriteX+1]   = SpriteX}
  6050.     @eine_Zeile:
  6051.       MOV SI,[end_min_start]         {SI = Yactual*2  }
  6052.       ADD SI,DI                      {startx:=sprite[WORD PTR sprite[L]+  }
  6053.       MOV DI,SI                      {               (Yactual+starty)*2]  }
  6054.       ADD SI,[Left]
  6055.       MOV SI,[SI]                    {SI = startx, DI = (Yaktuell+starty)*2}
  6056.       MOV AX,BP                      
  6057.       ADD AX,SI                      {AX=ScreenStartX:=SpriteX+startx       }
  6058.       CMP AX,XMAX                    {if ScreenStartX>xmax then skip_zeile    }
  6059.       JG @ToZeile_fertig
  6060.       MOV CX,SI                      {CX=startx}
  6061.       OR AX,AX                       {lecutoff_in_points:=startx }
  6062.       JGE @L1                        {if ScreenStartX<0 then     }
  6063.       SUB SI,AX                      { [dec(startx,ScreenStartX)     }
  6064.       XOR AX,AX                      {  ScreenStartX:=0              }
  6065.       MOV CX,BP                      {  lecutoff_in_points:=-SpriteX]  }
  6066.       NEG CX
  6067.     @L1:                             {CX=[licutoff_]= licutoff_in_points,  }
  6068.       MOV [licutoff_],CX             {SI = startx, AX = ScreenStartX       }
  6069.       ADD DI,[Right]
  6070.       MOV DI,[DI]                    {DI=endx:=sprite[WORD PTR sprite[R]+  }
  6071.                                      {                (Yactual+starty)*2]  }
  6072.       MOV DX,BP                      
  6073.       NEG DX                         {DX = -SpriteX }
  6074.       MOV BP,DI
  6075.       SUB BP,SI                      {BP = endx-startx }
  6076.       SUB DX,DI
  6077.       ADD DX,XMAX                    {DX=overhang :=xmax-(SpriteX+endx) }
  6078.       JNS @kein_Ueberhang_rechts
  6079.       ADD BP,DX
  6080.     @kein_Ueberhang_rechts:          {BP = visible width of this row -1    }
  6081.       OR BP,BP
  6082.       JNS @L6
  6083.     @ToZeile_fertig:
  6084.       JMP @Zeile_fertig              {if width<=0 then skip_zeile  }
  6085.     @L6:
  6086.       ADD BP,4
  6087.  
  6088.       { AX               = ScreenStartX    }
  6089.       { BX=[zeilenadr]   = (endy+SpriteY)*LINESIZE }
  6090.       { CX=[licutoff_]   = lecutoff_in_points }
  6091.       {(DX               = (negative) overhang (if value<0) )     }
  6092.       {(SI               = startx) }
  6093.       {(DI               = endx) }
  6094.       { BP               = width of this row in pixels +3        }
  6095.       { DS               = ^sprite data}
  6096.       { ES               = ^graphic page}
  6097.       { [@max_Breite+1]  = max_width_in_groups_of_4) }
  6098.       { [end_min_start]  = (endy-starty)*2 =Yaktuell*2}
  6099.       { [@Starty_2+1]    = starty*2}
  6100.       { [@max_Breite+1]  = max_width_in_groups_of_4,  }
  6101.       { [@akt_SpriteX+1] = SpriteX}
  6102.       MOV [bildx],AX                 {save ScreenStartX      }
  6103.       MOV DX,CX                      {DX = lecutoff_in_points }
  6104.       MOV CX,BP
  6105.       SHR CX,1
  6106.       SHR CX,1                       {CX = width DIV 4 }
  6107.       JCXZ @Plane1
  6108.  
  6109.       {SI=source pointer:=sprite[WORD PTR (lecutoff_in_points+0 AND 3)*2 }
  6110.       {                       +(lecutoff_in_points+0) DIV 4 +yoffset   }
  6111.       MOV SI,DX
  6112.       AND SI,3                       
  6113.       SHL SI,1                       {SI = ((lecutoff_in_points+0) AND 3)*2 }
  6114.       MOV SI,[SI]                    
  6115.       MOV DI,DX
  6116.       SHR DI,1
  6117.       SHR DI,1
  6118.       ADD SI,DI
  6119.       ADD SI,[yoffset_]              {SI = sprite[WORD PTR (licutoff_...)] }
  6120.                                      {     +(lecutoff_in_points+i) DIV 4   }
  6121.                                      {     +yoffset                        }
  6122.  
  6123.       {DI=dest.ptr:=(ScreenStartX+0) DIV 4 +zeilenadr      }
  6124.       MOV DI,AX                      {DI = ScreenStartX     }
  6125.       SHR DI,1
  6126.       SHR DI,1
  6127.       ADD DI,BX
  6128.       MOV BL,AL
  6129.       AND BX,3                       {BX = (ScreenStartX+i) AND 3     }
  6130.       MOV AH,Translate[BX]           {AH = 1,2,4,8 for BX=0,1,2,3     }
  6131.       MOV AL,2
  6132.       MOV DX,3C4h
  6133.       OUT DX,AX                      {choose plane    }
  6134.  
  6135.       XCHG BX,DI
  6136.       {copy CX bytes from DS:SI to ES:BX         }
  6137.       {this is the address where the data transfer routine will be patched!}
  6138.     @Patch1:
  6139.       db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  6140.  
  6141.     @Plane1:
  6142.       MOV DX,[bildx]
  6143.       INC DX                         {DX = ScreenStartX+1    }
  6144.       MOV BX,DX
  6145.       SHR BX,1
  6146.       SHR BX,1                       {BX=dest. ptr :=(ScreenStartX+1)     }
  6147.       ADD BX,[zeilenadr]             {                DIV 4 +zeilenadr    }
  6148.       MOV CX,BP
  6149.       DEC CX                         {CX = width of this line +3 -1  }
  6150.       SHR CX,1
  6151.       SHR CX,1                       {CX = bytes_to_move for i=1   }
  6152.       JCXZ @Plane2
  6153.       MOV DI,[licutoff_]
  6154.       INC DI                         {DI = (lecutoff_in_points+1)  }
  6155.       MOV SI,DI
  6156.       AND SI,3
  6157.       SHL SI,1                       {SI = ((lecutoff_in_points+1) AND 3)*2 }
  6158.       MOV SI,[SI]                    {SI = sprite[WORD PTR licutoff_...]  }
  6159.       SHR DI,1                       {     +(lecutoff_in_points+1) DIV 4  }
  6160.       SHR DI,1                       {     +yoffset                       }
  6161.       ADD SI,DI
  6162.       ADD SI,[yoffset_]              {SI = source pointer, }
  6163.                                      {DI = (lecutoff_in_points+1) DIV 4  }
  6164.  
  6165.       MOV DI,DX                      {DI = ScreenStartX+1    }
  6166.       AND DI,3                       {DI = (ScreenStartX+1) AND 3     }
  6167.       MOV AH,Translate[DI]           {load mask for port-access   }
  6168.       MOV AL,2
  6169.       MOV DX,3C4h                    {select plane   }
  6170.       OUT DX,AX
  6171.  
  6172.       {this is the address where the data transfer routine will be patched!}
  6173.     @Patch2:
  6174.       db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  6175.  
  6176.     @Plane2:
  6177.       MOV DX,[bildx]
  6178.       ADD DX,2
  6179.       MOV BX,DX
  6180.       SHR BX,1
  6181.       SHR BX,1
  6182.       ADD BX,[zeilenadr]
  6183.       MOV CX,BP
  6184.       SUB CX,2
  6185.       SHR CX,1
  6186.       SHR CX,1
  6187.       JCXZ @Plane3
  6188.       MOV DI,[licutoff_]
  6189.       ADD DI,2
  6190.       MOV SI,DI
  6191.       AND SI,3
  6192.       SHL SI,1
  6193.       MOV SI,[SI]
  6194.       SHR DI,1
  6195.       SHR DI,1
  6196.       ADD SI,DI
  6197.       ADD SI,[yoffset_]
  6198.  
  6199.       MOV DI,DX                      {DI = ScreenStartX+2    }
  6200.       AND DI,3                       {DI = (ScreenStartX+1) AND 3     }
  6201.       MOV AH,Translate[DI]
  6202.       MOV AL,2
  6203.       MOV DX,3C4h
  6204.       OUT DX,AX
  6205.  
  6206.       {this is the address where the data transfer routine will be patched!}
  6207.     @Patch3:
  6208.       db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  6209.  
  6210.     @Plane3:
  6211.       MOV DX,[bildx]
  6212.       ADD DX,3
  6213.       MOV BX,DX
  6214.       SHR BX,1
  6215.       SHR BX,1
  6216.       ADD BX,[zeilenadr]
  6217.       MOV CX,BP
  6218.       SUB CX,3
  6219.       SHR CX,1
  6220.       SHR CX,1
  6221.       JCXZ @Zeile_fertig
  6222.       MOV DI,[licutoff_]
  6223.       ADD DI,3
  6224.       MOV SI,DI
  6225.       AND SI,3
  6226.       SHL SI,1
  6227.       MOV SI,[SI]
  6228.       SHR DI,1
  6229.       SHR DI,1
  6230.       ADD SI,DI
  6231.       ADD SI,[yoffset_]
  6232.  
  6233.       MOV DI,DX                      {DI = ScreenStartX+3    }
  6234.       AND DI,3                       {DI = (ScreenStartX+1) AND 3     }
  6235.       MOV AH,Translate[DI]
  6236.       MOV AL,2
  6237.       MOV DX,3C4h
  6238.       OUT DX,AX
  6239.  
  6240.       {this is the address where the data transfer routine will be patched!}
  6241.     @Patch4:
  6242.       db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
  6243.  
  6244.     @Zeile_fertig:
  6245.       MOV AX,[yoffset_]
  6246.     @max_Breite:
  6247.       SUB AX,1234
  6248.       MOV [yoffset_],AX
  6249.       MOV BX,[zeilenadr]
  6250.       SUB BX,LINESIZE
  6251.       MOV [zeilenadr],BX
  6252.       SUB WORD PTR [end_min_start],2
  6253.       JS @Sprite_fertig
  6254.  
  6255.     @Starty_2:
  6256.       MOV DI,1234
  6257.     @akt_SpriteX:
  6258.       MOV BP,1234
  6259.       JMP @eine_Zeile
  6260.  
  6261.     @Sprite_fertig:
  6262.       POP SI
  6263.       MOV AX,SEG @Data
  6264.       MOV DS,AX
  6265.  
  6266.     @noSprite:
  6267.       DEC SI
  6268.       DEC SI
  6269.       JS @fertig
  6270.       JMP @zeichne
  6271.     @fertig:
  6272.  
  6273.       POP BP
  6274.  
  6275.     {The graphic page is now ready to be displayed:                        }
  6276.       cli
  6277.       mov  dx,StatusReg
  6278.  
  6279.     {Wait for "display enable"=0 (that is: active), so that page flipping  }
  6280.     {for HB/LB takes place while displaying the same page:}
  6281.  
  6282.     @WaitNotHSyncLoop:
  6283.       in   al,dx
  6284.       and  al,1
  6285.       jnz  @WaitNotHSyncLoop
  6286.     @WaitHSyncLoop:
  6287.       in   al,dx
  6288.       and  al,1
  6289.       jz   @WaitHSyncLoop
  6290.     
  6291.       MOV DX,CRTAddress          {CRT-Controller}
  6292.       MOV AL,$0D                 {LB-startaddress-register}
  6293.       OUT DX,AL
  6294.       INC DX
  6295.                                  { realize "AX:=Offset_Adr[Page]":    }
  6296.       MOV SI,PAGE                {page value *2 (word-sized entries!)}
  6297.       MOV BX,SI                  {save page value to BX! }
  6298.       SHL SI,1                   {add start address of array to that   }
  6299.       ADD SI,OFFSET Offset_Adr-StartIndex*2  {evtl. correct displacement    }
  6300.       LODSW                      {and fetch value}
  6301.       OUT DX,AL                  {set LB of new starting address  }
  6302.       DEC DX
  6303.       MOV AL,$0C
  6304.       OUT DX,AL
  6305.       INC DX
  6306.       MOV AL,AH                  {set HB of new starting address  }
  6307.       OUT DX,AL
  6308.       STI
  6309.  
  6310.       NEG BX       {new PAGE-value := 1-old PAGE-value, that is:  }
  6311.       ADD BX,1     {IF PAGE=0 THEN PAGE:=1 ELSE (PAGE=1) PAGE:=0 }
  6312.       MOV PAGE,BX
  6313.  
  6314.       SHL BX,1     {new PAGEADR-value := Segment_Adr[PAGE]  }
  6315.       ADD BX,OFFSET Segment_Adr-StartIndex*2
  6316.       MOV AX,[BX]
  6317.       MOV PAGEADR,AX
  6318.  
  6319.       {Check whether the preset (min.) cycle time has passed:             }
  6320.     @L10:
  6321.       MOV AL,TimeFlag   {bit 7 = 0/1 for delay completed/not yet completed }
  6322.       AND AL,$80
  6323.       JE @L10
  6324.  
  6325.       {start time control mechanism for next cycle:   }
  6326.       MOV AL,IsAT                 {is this an AT or better? ($0/$80=yes/no)}
  6327.       OR AL,AL                    {timing mechanism only works on AT/386 }
  6328.       JNE @L11                    {otherwise: no timing mechanism!     }
  6329.       MOV TimeFlag,AL             {use AL=0 as starting value, too     }
  6330.       MOV DX,WORD PTR CycleTime   {store min. time for 1 cycle (micro- }
  6331.       MOV CX,WORD PTR CycleTime+2 {seconds: CX=HIGH-Word, DX=LOW-Word  }
  6332.       MOV BX,OFFSET TimeFlag      {ES:BX=pointer to TimeFlag, bit 7=0/1}
  6333.       MOV AX,DS                   {for: delay still lasts/has ended       }
  6334.       MOV ES,AX
  6335.       MOV AX,8300h                {start time control mechanism}
  6336.       INT 15h
  6337.     @L11:
  6338.  END
  6339. END;
  6340.  
  6341.  
  6342. FUNCTION LoadSprite(name:String; number:WORD):WORD;
  6343. { in: name   = name of the sprite file to load (type: "*.COD" / "*.LIB")  }
  6344. {     number = number for the first sprite of this file                   }
  6345. {out: number of the sprites read in from the file (0 = error occured)     }
  6346. {rem: The routine automatically detects whether the specified file con-   }
  6347. {     tains a single sprite or a complete sprite library and loads all    }
  6348. {     sprite data into the heap, in a way such that the address always    }
  6349. {     lies on a segment (=paragraph) boundary. These starting addresses   }
  6350. {     are then stored in table SPRITEAD[number]; if more than one sprite  }
  6351. {     has been loaded, they will be stored with consecutive numbers, that    }
  6352. {     is, number+i  }
  6353. LABEL quit_loop;
  6354. VAR p1,p2:Pointer;
  6355.     len:LONGINT;
  6356.     f:File;
  6357.     count,Kopf:WORD;
  6358.     Header:SpriteHeader;
  6359. BEGIN
  6360.  count:=0;  {number of sprites already read in   }
  6361.  Kopf:=SizeOf(SpriteHeader);
  6362.  assign(f,name);
  6363.  {$I-} reset(f,1); {$I+}
  6364.  if (ioresult<>0)
  6365.   THEN BEGIN  {File doesn't exist or at least not with that path }
  6366.         Error:=Err_FileIO;
  6367.         loadSprite:=0; exit
  6368.        END;
  6369.  len:=filesize(f);  {determine file length}
  6370.  if (maxavail<len+16)
  6371.   THEN BEGIN
  6372.         Error:=Err_NotEnoughMemory;
  6373.         goto quit_loop;
  6374.        END;
  6375.  if (number=0) or (number>LoadMAX)
  6376.   THEN BEGIN
  6377.         Error:=Err_InvalidSpritenumber;
  6378.         goto quit_loop;
  6379.        END;
  6380.  WHILE NOT EOF(f) DO
  6381.  BEGIN
  6382.   {first, read in the sprite header:    }
  6383.   {$I-}     {load sprite header vià BLOCKREAD into the heap         }
  6384.   blockread(f,Header,Kopf);
  6385.   {$I+}
  6386.  
  6387.   IF (ioresult<>0)
  6388.    THEN BEGIN
  6389.          Error:=Err_FileIO;
  6390.          goto quit_loop;
  6391.         END;
  6392.   IF (Header.Kennung[1]<>'K') or (Header.Kennung[2]<>'R')
  6393.    THEN BEGIN
  6394.          Error:=Err_NoSprite;
  6395.          goto quit_loop;
  6396.         END;
  6397.   IF (Header.SpriteLength>MaxAvail+15)    {enough space left?  }
  6398.    THEN BEGIN
  6399.          Error:=Err_NotEnoughMemory;
  6400.          goto quit_loop;
  6401.         END;
  6402.  
  6403.   {Now read in the real sprite data:       }
  6404.   getmem(p1,Header.SpriteLength+15);       {get enough space       }
  6405.   IF (LONGINT(p1) mod 16)=0
  6406.    THEN p2:=p1                             {make p2 fall on segment boundary}
  6407.    ELSE LONGINT(p2):=LONGINT(p1) + (16-LONGINT(p1) mod 16);
  6408.  
  6409.   MOVE(Header,p2^,Kopf);  {store sprite header to heap  }
  6410.   LONGINT(p1):=LONGINT(p2)+Kopf;   {points exactly behind the header}
  6411.  
  6412.   {$I-}     {load the "rest" of the sprite     }
  6413.   blockread(f,p1^,Header.SpriteLength-Kopf);
  6414.   {$I+}
  6415.   IF (ioresult<>0)
  6416.    THEN BEGIN
  6417.          Error:=Err_FileIO;
  6418.          goto quit_loop;
  6419.         END;
  6420.  
  6421.   {assign it to the sprite number:}
  6422.   spritead[number+count]:=(longint(p2) shr 16)
  6423.                          +(longint(p2) and 65535) shr 4;
  6424.   INC(count);
  6425.  END;
  6426.  
  6427. quit_loop: ;
  6428.  close(f);
  6429.  loadSprite:=count
  6430. END;
  6431.  
  6432. FUNCTION LoadTile(name:STRING; number:BYTE):WORD;
  6433. { in: name   = name of the sprite file to load (type: "*.COD" / "*.LIB")  }
  6434. {     number = 0..255 = tile number for the file's first sprite           }
  6435. {out: number of the tiles read in from the file (0 = error occured)       }
  6436. {rem: The routine automatically detects whether the specified file con-   }
  6437. {     tains a single sprite or a complete sprite library and loads all    }
  6438. {     sprites, splits them into tiles and stores them into the 4th graphic  }
  6439. {     page, starting with the given number "number"                       }
  6440. {     Because a tile consists of 16x16 points, the sprites must be a mul- }
  6441. {     tiple of 16 points in each direction (x _and_ y)                     }
  6442. {     If the file contains more than one tile, they will be loaded by row,}
  6443. {     each row from left to right                                         }
  6444. LABEL quit_loop;
  6445. TYPE split=RECORD loword,hiword:WORD END;
  6446. VAR p1:Pointer;
  6447.     len,ad,p:LONGINT;
  6448.     f:File;
  6449.     count,Kopf,ZielOfs,step,yoffset:WORD;
  6450.     pSeg,pOfs:ARRAY[0..3] OF WORD;
  6451.     Breite_in_Tiles,Hoehe_in_Tiles,x,y,i,zeilen:BYTE;
  6452.     Header:SpriteHeader;
  6453. BEGIN
  6454.  count:=0;  {number of sprites already read in   }
  6455.  Kopf:=SizeOf(SpriteHeader);
  6456.  assign(f,name);
  6457.  {$I-} reset(f,1); {$I+}
  6458.  if (ioresult<>0)
  6459.   THEN BEGIN  {File doesn't exist or at least not with that path }
  6460.         Error:=Err_FileIO;
  6461.         LoadTile:=0; exit
  6462.        END;
  6463.  len:=filesize(f);  {determine file length}
  6464.  if (maxavail<len+16)
  6465.   THEN BEGIN
  6466.         Error:=Err_NotEnoughMemory;
  6467.         goto quit_loop;
  6468.        END;
  6469.  WHILE NOT EOF(f) DO
  6470.  BEGIN
  6471.   {first, read in the sprite header:    }
  6472.   {$I-}     {load sprite header vià BLOCKREAD into the heap         }
  6473.   blockread(f,Header,Kopf);
  6474.   {$I+}
  6475.  
  6476.   IF (ioresult<>0)
  6477.    THEN BEGIN
  6478.          Error:=Err_FileIO;
  6479.          goto quit_loop;
  6480.         END;
  6481.   IF (Header.Kennung[1]<>'K') or (Header.Kennung[2]<>'R')
  6482.    THEN BEGIN
  6483.          Error:=Err_NoTile;  {or Err_NoSprite!  }
  6484.          goto quit_loop
  6485.         END;
  6486.   IF (Header.Breite_in_4er_Gruppen MOD 4<>0) OR
  6487.      (Header.Hoehe_in_Zeilen MOD 16<>0)   {size a multiple of 16?    }
  6488.    THEN BEGIN
  6489.          Error:=Err_NoTile;
  6490.          goto quit_loop
  6491.         END
  6492.    ELSE BEGIN {yes, get number of tiles in that sprite file   }
  6493.          Breite_in_Tiles:=Header.Breite_in_4er_Gruppen SHR 2;
  6494.          Hoehe_in_Tiles :=Header.Hoehe_in_Zeilen SHR 4;
  6495.          step:=Breite_in_Tiles*4; {"step" needed for correct addressing}
  6496.         END;
  6497.   IF (Header.SpriteLength>MaxAvail)    {enough space left?  }
  6498.    THEN BEGIN
  6499.          Error:=Err_NotEnoughMemory;
  6500.          goto quit_loop;
  6501.         END;
  6502.  
  6503.   {Now read in the real sprite data:       }
  6504.   getmem(p1,Header.SpriteLength);      {get enough space       }
  6505.  
  6506.   {$I-}     {load the "rest" of the sprite     }
  6507.   blockread(f,p1^,Header.SpriteLength-Kopf);
  6508.   {$I+}
  6509.   IF (ioresult<>0)
  6510.    THEN BEGIN
  6511.          Error:=Err_FileIO;
  6512.          goto quit_loop;
  6513.         END;
  6514.  
  6515.   ad:=(LONGINT(split(p1).HiWord) SHL 4) + split(p1).LoWord - Kopf;
  6516.   FOR i:=0 TO 3 DO
  6517.    BEGIN
  6518.     p:=ad+Header.Zeiger_auf_Plane[i]; pSeg[i]:=p SHR 4; pOfs[i]:=p AND $F;
  6519.    END;
  6520.  
  6521.   FOR y:=0 TO Pred(Hoehe_in_Tiles) DO
  6522.    BEGIN
  6523.     yoffset:=y*Breite_in_Tiles*16*(16 DIV 4);
  6524.     FOR x:=0 TO Pred(Breite_in_Tiles) DO
  6525.      BEGIN
  6526.       IF count+number>255
  6527.        THEN BEGIN
  6528.              Error:=Err_InvalidTileNumber;
  6529.              goto quit_loop
  6530.             END;
  6531.       ZielOfs:=(number+count) SHL 6;
  6532.       FOR i:=0 TO 3 DO
  6533.        BEGIN
  6534.         PORTW[$3C4]:=(TranslateTab[i] SHL 8) + 2;
  6535.         FOR zeilen:=0 TO 15 DO
  6536.          BEGIN
  6537.           move(mem[pSeg[i]:pOfs[i] + yoffset + zeilen*step + x*(16 DIV 4)],
  6538.                mem[SCROLLADR:ZielOfs + zeilen*(16 DIV 4)],
  6539.                16 DIV 4);
  6540.          END;
  6541.        END;
  6542.  
  6543.       INC(count);
  6544.      END;
  6545.    END;
  6546.   Dispose(p1);
  6547.  END;
  6548.  
  6549. quit_loop: ;
  6550.  close(f);
  6551.  LoadTile:=count
  6552. END;
  6553.  
  6554. PROCEDURE SetBackgroundScrollRange(x1,y1,x2,y2:INTEGER);
  6555. { in: (x1,y1) = upper left corner of the area (in virtual coord.)}
  6556. {     (x2,y2) = dto., lower right corner}
  6557. {out: (BackX1,BackY1), (BackX2,BackY2) = coord., rounded to 16'grid        }
  6558. {     XTiles, YTiles = width and height of the chosen range in tiles       }
  6559. {rem: The upper left corner will be torn to the upper left, the lower      }
  6560. {     right corner to the lower right!}
  6561. {     Obviously, calling the routine only makes sense when using SCROLLING }
  6562. {     as background mode!     }
  6563. BEGIN
  6564.  BackX1:=x1 AND $FFF0; BackX2:=x2 OR $F;
  6565.  BackY1:=y1 AND $FFF0; BackY2:=y2 OR $F;
  6566.  xtiles:=succ(BackX2-BackX1) shr 4;
  6567.  ytiles:=succ(BackY2-BackY1) shr 4;
  6568.  IF (xtiles OR ytiles)<=0
  6569.   THEN Error:=Err_InvalidCoordinates
  6570.  ELSE IF xtiles*ytiles>MaxTiles
  6571.   THEN Error:=Err_BackgroundToBig;
  6572. END;
  6573.  
  6574. PROCEDURE SetBackgroundMode(mode:BYTE);
  6575. { in: mode = wanted background mode, STATIC or SCROLLING        }
  6576. {out: Backgroundmode = set mode, STATIC/SCROLLING      }
  6577. BEGIN
  6578.  IF (mode<>STATIC) AND (mode<>SCROLLING)
  6579.   THEN Error:=Err_InvalidMode
  6580.   ELSE Backgroundmode:=mode
  6581. END;
  6582.  
  6583. PROCEDURE PutTile(x,y:INTEGER; TileNr:BYTE);
  6584. { in: x,y   = virtual coordinates where the tile shall be placed          }
  6585. {     TileNr= number of the tile to be placed  }
  6586. {out: - }
  6587. {rem: The point (x,y) first gets rounded to a grid with mesh 16  }
  6588. {     Calling this routine only makes sense when using SCROLLING}
  6589. {     as background mode!     }
  6590. VAR index:WORD;
  6591. BEGIN
  6592.  ASM
  6593.     MOV AX,x        {compute relative X-distance from left edge of   }
  6594.     SUB AX,BackX1   {the defined area and store it to "x", formula:  }
  6595.     SAR AX,1        { x:=((x AND $FFF0)-BackX1) DIV 16 (not: SHR 4)! }
  6596.     SAR AX,1        {"AND $FFF0" isn't needed, because in BackX1, the  }
  6597.     SAR AX,1        {last hex-digit is $0!                           }
  6598.     SAR AX,1
  6599.     MOV x,AX
  6600.  
  6601.     MOV AX,y        {dto. for distance between the y-coordinate and   }
  6602.     SUB AX,BackY1   {top boundary: y:=((y AND $FFF0)-BackY1) DIV 16          }
  6603.     SAR AX,1
  6604.     SAR AX,1
  6605.     SAR AX,1
  6606.     SAR AX,1
  6607.     MOV y,AX
  6608.  END;
  6609.  
  6610.  IF (x<0) OR (x>=XTiles) OR (y<0) OR (y>=YTiles)
  6611.   THEN Error:=Err_InvalidCoordinates
  6612.   ELSE BEGIN {a tile row has width XTiles, each tile consists of 16x16 points}
  6613.         index:=y*XTiles+x;  {to be exact: (x MOD XTiles)}
  6614.         BackTile[Succ(index)]:=TileNr;  {"succ", to hold free BackTile[0]   }
  6615.        END;
  6616. END;
  6617.  
  6618. PROCEDURE SetOffscreenTile(TileNr:BYTE);
  6619. { in: TileNr= number of the tile to be placed  }
  6620. {out: - }
  6621. {rem: all screen parts, which lie outside the window specified by  }
  6622. {     SetBackgroundScrollRange will become the tile TileNr as      }
  6623. {     pattern           }
  6624. {     Calling this routine only makes sense when using SCROLLING   }
  6625. {     as background mode!     }
  6626. BEGIN
  6627.  BackTile[0]:=TileNr
  6628. END;
  6629.  
  6630. PROCEDURE SetModeByte(Sp:WORD; M:BYTE);
  6631. { in: Sp = spriteLOADnumber, which mode byte shall be changed       }
  6632. {out: M  = method, which shall be used to display Sp from now on:   }
  6633. {          Display_NORMAL, Display_FAST, Display_SHADOW or          }
  6634. {          Display_SHADOWEXACT                                      }
  6635. {rem: If the sprite doesn't exist yet (or the mode isn't allowed),  }
  6636. {     nothing will happen at all!                                    }
  6637. VAR ad:WORD;
  6638. BEGIN
  6639.  ad:=SPRITEAD[Sp];
  6640.  IF ad=0 THEN Error:=Err_InvalidSpriteNumber {sprite must already be loaded }
  6641.  ELSE IF (M<Display_NORMAL) OR (M>Display_SHADOWEXACT)
  6642.   THEN Error:=Err_InvalidMode  {only these 4 modes are allowed }
  6643.  ELSE MEM[ad:Modus]:=M
  6644. END;
  6645.  
  6646. FUNCTION GetModeByte(Sp:WORD):BYTE;
  6647. { in: Sp = spriteLOADnumber, which mode byte shall be determined   }
  6648. {out: actually set display method for sprite Sp: Display_NORMAL,   }
  6649. {     Display_FAST, Display_SHADOW, Display_SHADOWEXACT or         }
  6650. {     Display_UNKNOW, if the sprite hasn't been loaded, yet!       }
  6651. VAR ad:WORD;
  6652. BEGIN
  6653.  ad:=SPRITEAD[Sp];
  6654.  IF (ad=0)
  6655.   THEN GetModeByte:=Display_UNKNOWN     {sprite not yet loaded    }
  6656.   ELSE GetModeByte:=MEM[SPRITEAD[Sp]:Modus]
  6657. END;
  6658.  
  6659. PROCEDURE FillPage(pa,color:Byte);
  6660. { in: pa    = the page which shall be filled (0..3)     }
  6661. {     color = color with which to fill the page}
  6662. {out: graphic page "pa" has been filled with color "Color"     }
  6663. {rem: Only "pa" values of 0,1 and BACKGNDPAGE make sense, but  }
  6664. {     SCROLLPAGE is allowed, too      }
  6665. BEGIN
  6666.  IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE) AND (pa<>SCROLLPAGE)
  6667.   THEN Error:=Err_InvalidPageNumber
  6668.   ELSE BEGIN
  6669.         portw[$3C4]:=$0F02; {use MapMask register to select all 4 planes   }
  6670.         fillchar(MEM[Segment_Adr[pa]:0],PAGESIZE,Color)
  6671.        END;
  6672. END;
  6673.  
  6674. PROCEDURE FillBackground(color:BYTE);
  6675. { in: color = color for filling the background page BACKGNDPAGE        }
  6676. {out: The graphic page BACKGNDPAGE has been filled with color "Color" }
  6677. {rem: Using the routine only makes sense when using background mode STATIC   }
  6678. BEGIN
  6679.  FillPage(BACKGNDPAGE,color)
  6680. END;
  6681.  
  6682. PROCEDURE GetBackgroundFromPage(pa:Byte);
  6683. {in : pa = 0 or 1   }
  6684. {out: -             }
  6685. {rem: The background page BACKGNDPAGE has been filled with the con-   }
  6686. {     tents of the supplied graphic page.}
  6687. {     Using the routine only makes sense when using background mode STATIC   }
  6688. VAR p:POINTER;
  6689. BEGIN
  6690.  IF (pa<>0) AND (pa<>1)
  6691.   THEN Error:=Err_InvalidPageNumber
  6692.   ELSE BEGIN
  6693.         portw[$3c4]:=$0f02; {use MapMask register to select all 4 planes  }
  6694.         port[$3ce]:=5;      {write mode 1   }
  6695.         port[$3cf]:=port[$3cf] OR 1;   {or directly :=$41}
  6696.         p:=Ptr(Segment_Adr[pa],$0000);
  6697.         MOVE(p^,MEM[BACKGNDADR:0],PAGESIZE);
  6698.         portw[$3cf]:=port[$3cf] and $FC; {write mode 0 (or directly :=$40) }
  6699.        END;
  6700. END;
  6701.  
  6702. PROCEDURE WritePage(name:STRING; pa:BYTE);
  6703. { in: name     = file name for the picture to store    }
  6704. {     pa       = page to store (0..2)         }
  6705. {     PAGESIZE = size of one bitplane         }
  6706. {     PICHeader= tag for picture file                    }
  6707. {out: - }
  6708. {rem: The picture at page "pa" has been stored (as bitmap) to file "name"    }
  6709. {     This file has size 4*PAGESIZE+3 = 64003 bytes: 320x200 points ,        }
  6710. {     1 byte plus length(PICHeader) as tag     }
  6711. VAR f:FILE;
  6712.     i,oldMode:BYTE;
  6713.     fehler:BOOLEAN;
  6714. BEGIN
  6715.  IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE)
  6716.   THEN BEGIN
  6717.         Error:=Err_InvalidPageNumber; exit
  6718.        END;
  6719.  {$I-}
  6720.  Assign(f,name); fehler:=IOResult<>0;
  6721.  Rewrite(f,1);   fehler:=fehler OR (IOResult<>0);
  6722.  BlockWrite(f,PICHeader[1],Length(PICHeader));
  6723.  fehler:=fehler OR (IOResult<>0);
  6724.  {$I+}
  6725.  IF fehler
  6726.   THEN BEGIN
  6727.         {$I-} Close(f); {$I+}
  6728.         Error:=Err_FileIO; exit
  6729.        END;
  6730.  port[$3ce]:=5;       {save old read-/write mode      }
  6731.  oldMode:=port[$3cf];
  6732.  port[$3cf]:=$40;     {set read mode 0   }
  6733.  FOR i:=0 TO 3 DO
  6734.   BEGIN
  6735.    portw[$3CE]:=4+(i shl 8); {select read plane   }
  6736.    {$I-}
  6737.    BlockWrite(f,mem[Segment_Adr[pa]:0],PAGESIZE);
  6738.    {$I+}
  6739.    fehler:=fehler OR (IOResult<>0);
  6740.   END;
  6741.  {$I-}
  6742.  Close(f);
  6743.  {$I+}
  6744.  fehler:=fehler OR (IOResult<>0);
  6745.  port[$3ce]:=5;       {restore old read-/write mode   }
  6746.  port[$3cf]:=oldMode;
  6747.  IF fehler THEN Error:=Err_FileIO
  6748. END;
  6749.  
  6750. PROCEDURE LoadPage(name:STRING; pa:BYTE);
  6751. { in: name     = file name for the picture to load}
  6752. {     pa       = destination page to be loaded with the picture (0..2) }
  6753. {     PAGESIZE = size of one bitplane      }
  6754. {     PICHeader= tag for picture files     }
  6755. {out: - }
  6756. {rem: The bitmap-picture in file "name" has been loaded into page "pa"     }
  6757. VAR f:FILE;
  6758.     i,oldMode:BYTE;
  6759.     fehler:BOOLEAN;
  6760.     s:STRING[3];
  6761.     splane:WORD;
  6762.     p1,p2:POINTER;
  6763. TYPE tempArray=ARRAY[1..PAGESIZE] OF BYTE;
  6764. VAR  temp:^tempArray;
  6765. BEGIN
  6766.  IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE)
  6767.   THEN BEGIN
  6768.         Error:=Err_InvalidPageNumber; exit
  6769.        END;
  6770.  {$I-}
  6771.  Assign(f,name); fehler:=IOResult<>0;
  6772.  Reset(f,1);     fehler:=fehler OR (IOResult<>0);
  6773.  s[0]:=PICHeader[0];
  6774.  BlockRead(f,s[1],Length(PICHeader)); fehler:=fehler OR (IOResult<>0);
  6775.  {$I+}
  6776.  IF fehler
  6777.   THEN BEGIN
  6778.         {$I-} Close(f); {$I+}
  6779.         Error:=Err_FileIO; exit
  6780.        END
  6781.   ELSE IF (FileSize(f)<>4*PAGESIZE+Length(PICHeader)) OR (s<>PICHeader)
  6782.   THEN BEGIN
  6783.         {$I-} Close(f); {$I+}
  6784.         Error:=Err_NoPicture; exit
  6785.        END;
  6786.  ASM cli END;
  6787.  port[$3ce]:=5;       {save old read-/write mode      }
  6788.  oldMode:=port[$3cf];
  6789.  New(temp);
  6790.  ASM sti END;
  6791.  FOR i:=0 TO 3 DO
  6792.   BEGIN
  6793.    {$I-}
  6794.    BlockRead(f,temp^[1],PAGESIZE);
  6795.    {$I+}
  6796.    fehler:=fehler OR (IOResult<>0);
  6797.    splane:=2+(TranslateTab[i] shl 8); {which write plane   }
  6798.    p1:=@temp^[1];
  6799.    p2:=ptr(Segment_Adr[pa],0);
  6800.    ASM
  6801.     cli
  6802.     mov dx,$3CE      {select write mode 0   }
  6803.     mov ax,$4005
  6804.     out dx,ax
  6805.  
  6806.     mov ax,splane    {select write plane    }
  6807.     mov dx,$3C4
  6808.     out dx,ax
  6809.  
  6810.     les di,p2
  6811.     lds si,p1
  6812.     mov cx,PAGESIZE SHR 1
  6813.     rep movsw
  6814.  
  6815.     mov ax,SEG @DATA
  6816.     mov ds,ax
  6817.     sti
  6818.    END;
  6819.  
  6820.   END;
  6821.  Dispose(temp);
  6822.  {$I-}
  6823.  Close(f);
  6824.  {$I+}
  6825.  fehler:=fehler OR (IOResult<>0);
  6826.  portw[$3ce]:=oldMode SHL 8 + 5; {restore old read-/write mode   }
  6827.  IF fehler THEN Error:=Err_FileIO
  6828. END;
  6829.  
  6830. PROCEDURE WriteBackgroundPage(name:STRING);
  6831. { in: name       = file name for the background picture to store    }
  6832. {     BACKGNDPAGE= page to be saved (=2)      }
  6833. {     PAGESIZE   = size of one bitplane       }
  6834. {     PICHeader  = tag for picture file                    }
  6835. {out: - }
  6836. {rem: The picture of the background page has been stored as file "name" }
  6837. {     This file has size 4*PAGESIZE+3 = 64003 bytes: 320x200 points     }
  6838. {     at 1 byte each, plus length(PICHeader) as tag  }
  6839. {     Using the routine only makes sense when using background mode STATIC     }
  6840. BEGIN
  6841.  WritePage(name,BACKGNDPAGE)
  6842. END;
  6843.  
  6844. PROCEDURE LoadBackgroundPage(name:STRING);
  6845. { in: name       = file name for the background picture to load}
  6846. {     BACKGNDPAGE= destination page, in which to load the picture (=2) }
  6847. {     PAGESIZE = size of one bitplane      }
  6848. {     PICHeader= tag for picture files     }
  6849. {out: - }
  6850. {rem: The bitmap-picture contained in file "name" has been loaded to the }
  6851. {     background page BACKGNDPAGE}
  6852. {     Using the routine only makes sense when using background mode STATIC}
  6853. BEGIN
  6854.  LoadPage(name,BACKGNDPAGE)
  6855. END;
  6856.  
  6857. PROCEDURE FadeIn(pa,ti,style:WORD);
  6858. { in: pa = page which shall be faded onto the actually visible page  }
  6859. {     ti = time in milliseconds for this action             }
  6860. {     style = algorithm which shall be used             }
  6861. {     1-PAGE = actually visible page  }
  6862. {out: Error = Err_InvalidFade, if illegal "style" value has been used     }
  6863. {rem: graphic mode must have been initialized already              }
  6864. {     most often, you will use pa=BACKGNDPAGE   }
  6865.  
  6866.   PROCEDURE WipeIn(pa,time:WORD);
  6867.   { in: pa    = page, which contents will be made visible       }
  6868.   {     time  = time (in millisceconds) for this action (approx.)            }
  6869.   {     1-PAGE= (visible) graphic page on which to draw         }
  6870.   {out: - }
  6871.   {rem: the contents of page "pa" has been copied to page 1-PAGE    }
  6872.   CONST hoehe =40; {divisor of Succ(YMAX)}
  6873.         breite=40; {divisor of Succ(XMAX)}
  6874.         br_x  =Succ(XMAX) DIV breite; {blocks in X-direction}
  6875.         br_y  =Succ(YMAX) DIV hoehe;  {blocks in Y-direction}
  6876.         n=hoehe*br_x; {number of executions of the delay loop  }
  6877.   VAR i,x,y,ploty,plotx:WORD;
  6878.       counter:WORD;
  6879.       ClockTicks:LONGINT ABSOLUTE $40:$6C;
  6880.       t:LONGINT;
  6881.       temp:REAL;
  6882.       p:POINTER;
  6883.   BEGIN
  6884.    t:=ClockTicks;
  6885.    counter:=0;
  6886.    temp:=0.0182*time/n;
  6887.    FOR i:=0 TO pred(hoehe) DO
  6888.     FOR x:=0 TO pred(br_x) DO
  6889.      BEGIN
  6890.       FOR y:=0 TO pred(br_y) DO
  6891.        BEGIN
  6892.         IF ODD(x)
  6893.          THEN ploty:=y*hoehe+i          +StartVirtualY
  6894.          ELSE ploty:=y*hoehe+(hoehe-1-i)+StartVirtualY;
  6895.         plotx:=x*breite +StartVirtualX;
  6896.         p:=GetImage(plotx,ploty,plotx+pred(breite),ploty,pa);
  6897.         PutImage(plotx,ploty,p,1-PAGE);
  6898.         FreeImageMem(p);
  6899.        END; {of FOR y}
  6900.       INC(counter);
  6901.       WHILE ClockTicks<t+counter*temp DO BEGIN END;
  6902.      END; {of FOR x}
  6903.   END;
  6904.  
  6905.   PROCEDURE Chaos(pa,time:WORD;m:BYTE);
  6906.   { in: pa    = page, which contents will be made visible       }
  6907.   {     time  = time (in millisceconds) for this action (approx.)            }
  6908.   {     m     = the way how this shall be done (1..14)}
  6909.   {     1-PAGE= (visible) graphic page on which to draw         }
  6910.   {out: - }
  6911.   {rem: the contents of page "pa" has been copied to page 1-PAGE    }
  6912.   CONST n=Succ(XMAX)*Succ(YMAX);  {number of screen pixels}
  6913.         {e.g., good values are sums of powers of 2 +1         }
  6914.         para:ARRAY[1..14] OF WORD=
  6915.          (13477,65,337,129,257,513,769,1025,481,4097,5121,177,16385,16897);
  6916.   VAR i,k,x,y:WORD;
  6917.       counter:WORD;
  6918.       ClockTicks:LONGINT ABSOLUTE $40:$6C;
  6919.       t:LONGINT;
  6920.       temp:REAL;
  6921.       rand:WORD;
  6922.   BEGIN
  6923.    t:=ClockTicks;
  6924.    counter:=0;
  6925.    rand:=0;
  6926.    IF (m<1) OR (m>14) THEN m:=1;
  6927.    k:=para[m];
  6928.    temp:=0.0182*time/n;
  6929.    FOR i:=0 TO 65535 DO
  6930.     BEGIN
  6931.      ASM {compute: "x:=rand MOD 320" and "y:=rand DIV 320" }
  6932.       XOR DX,DX
  6933.       MOV AX,rand
  6934.       MOV BX,XMAX+1
  6935.       DIV BX
  6936.       MOV y,AX
  6937.       MOV x,DX
  6938.      END;
  6939.      IF y<=YMAX
  6940.       THEN PutPixel(StartVirtualX+x,StartVirtualY+y,
  6941.                     PageGetPixel(StartVirtualX+x,StartVirtualY+y,pa));
  6942.      ASM {compute: rand:=rand*k+1 }
  6943.       MOV AX,rand
  6944.       MUL k
  6945.       INC AX
  6946.       MOV rand,AX
  6947.      END;
  6948.      INC(counter);
  6949.      WHILE ClockTicks<t+counter*temp DO BEGIN END;
  6950.     END; {of FOR i}
  6951.   END;
  6952.  
  6953.   PROCEDURE SweepVertical(pa,time:WORD; down:BOOLEAN);
  6954.   { in: pa    = page, which contents will be made visible       }
  6955.   {     time  = time (in millisceconds) for this action (approx.)            }
  6956.   {     down  = TRUE/FALSE for: from bottom to top/vice versa   }
  6957.   {     1-PAGE= (visible) graphic page on which to draw         }
  6958.   {out: - }
  6959.   {rem: the contents of page "pa" has been copied to page 1-PAGE    }
  6960.   CONST n=Succ(YMAX);      {number of executions of the delay loop  }
  6961.   VAR y,counter:WORD;
  6962.       ClockTicks:LONGINT ABSOLUTE $40:$6C;
  6963.       t:LONGINT;
  6964.       temp:REAL;
  6965.       oldColor,step:BYTE;
  6966.       p:POINTER;
  6967.   BEGIN
  6968.    oldColor:=Color;
  6969.    Color:=white;
  6970.    t:=ClockTicks;
  6971.    counter:=0;
  6972.    temp:=0.0182*time/n;
  6973.    IF down
  6974.     THEN FOR y:=0+StartVirtualY TO YMAX+StartVirtualY DO
  6975.           BEGIN
  6976.            Line(StartVirtualX,y,StartVirtualX+XMAX,y,1-PAGE);
  6977.            INC(counter);
  6978.            WHILE ClockTicks<t+counter*temp DO BEGIN END;
  6979.            p:=GetImage(StartVirtualX,y,StartVirtualX+XMAX,y,pa);
  6980.            PutImage(StartVirtualX,y,p,1-PAGE);
  6981.            FreeImageMem(p);
  6982.           END
  6983.     ELSE FOR y:=YMAX+StartVirtualY DOWNTO 0+StartVirtualY DO
  6984.           BEGIN
  6985.            Line(StartVirtualX,y,StartVirtualX+XMAX,y,1-PAGE);
  6986.            INC(counter);
  6987.            WHILE ClockTicks<t+counter*temp DO BEGIN END;
  6988.            p:=GetImage(StartVirtualX,y,StartVirtualX+XMAX,y,pa);
  6989.            PutImage(StartVirtualX,y,p,1-PAGE);
  6990.            FreeImageMem(p);
  6991.           END;
  6992.    Color:=oldColor
  6993.   END;
  6994.  
  6995.   PROCEDURE SweepHorizontal(pa,time:WORD; left_to_right:BOOLEAN);
  6996.   { in: pa    = page, which contents will be made visible       }
  6997.   {     time  = time (in millisceconds) for this action (approx.)            }
  6998.   {     left_to_right=TRUE/FALSE for: from left to right/vice versa   }
  6999.   {     1-PAGE= (visible) graphic page on which to draw         }
  7000.   {out: - }
  7001.   {rem: the contents of page "pa" has been copied to page 1-PAGE    }
  7002.   CONST n=Succ(XMAX);      {number of executions of the delay loop  }
  7003.   VAR x,counter:WORD;
  7004.       ClockTicks:LONGINT ABSOLUTE $40:$6C;
  7005.       t:LONGINT;
  7006.       temp:REAL;
  7007.       oldColor,step:BYTE;
  7008.       p:POINTER;
  7009.   BEGIN
  7010.    oldColor:=Color;
  7011.    Color:=white;
  7012.    t:=ClockTicks;
  7013.    counter:=0;
  7014.    temp:=0.0182*time/n;
  7015.    IF left_to_right
  7016.     THEN FOR x:=0+StartVirtualX TO XMAX+StartVirtualX DO
  7017.           BEGIN
  7018.            Line(x,StartVirtualY,x,StartVirtualY+YMAX,1-PAGE);
  7019.            INC(counter);
  7020.            WHILE ClockTicks<t+counter*temp DO BEGIN END;
  7021.            p:=GetImage(x,StartVirtualY,x,StartVirtualY+YMAX,pa);
  7022.            PutImage(x,StartVirtualY,p,1-PAGE);
  7023.            FreeImageMem(p);
  7024.           END
  7025.     ELSE FOR x:=XMAX+StartVirtualX DOWNTO 0+StartVirtualX DO
  7026.           BEGIN
  7027.            Line(x,StartVirtualY,x,StartVirtualY+YMAX,1-PAGE);
  7028.            INC(counter);
  7029.            WHILE ClockTicks<t+counter*temp DO BEGIN END;
  7030.            p:=GetImage(x,StartVirtualY,x,StartVirtualY+YMAX,pa);
  7031.            PutImage(x,StartVirtualY,p,1-PAGE);
  7032.            FreeImageMem(p);
  7033.           END;
  7034.    Color:=oldColor
  7035.   END;
  7036.  
  7037.   PROCEDURE ScrollVertical(pa,time:WORD; up:BOOLEAN);
  7038.   { in: pa    = page, which contents will be made visible       }
  7039.   {     time  = time (in millisceconds) for this action (approx.)            }
  7040.   {     up    = TRUE/FALSE for: from bottom to top/vice versa   }
  7041.   {     1-PAGE= (visible) graphic page on which to draw         }
  7042.   {out: - }
  7043.   {rem: the contents of page "pa" has been copied to page 1-PAGE    }
  7044.   LABEL oneLine1,oneLine2;
  7045.   CONST n=Succ(YMAX);      {number of executions of the delay loop  }
  7046.   VAR counter:WORD;
  7047.       ClockTicks:LONGINT ABSOLUTE $40:$6C;
  7048.       t:LONGINT;
  7049.       temp:REAL;
  7050.   BEGIN
  7051.    t:=ClockTicks;
  7052.    counter:=0;
  7053.    temp:=0.0182*time/n;
  7054.    IF up
  7055.     THEN BEGIN {scroll upwards    }
  7056.           ASM
  7057.             MOV DX,3C4h
  7058.             MOV AX,0F02h  {access all 4 planes at once          }
  7059.             OUT DX,AX
  7060.             MOV DX,3CEh
  7061.             MOV AX,4105h  {set write mode 1  }
  7062.             OUT DX,AX
  7063.  
  7064.             MOV SI,1
  7065.             SUB SI,PAGE
  7066.             AND SI,1
  7067.             SHL SI,1
  7068.             ADD SI,OFFSET Segment_Adr-StartIndex*2
  7069.             LODSW
  7070.             MOV ES,AX     {ES:=Segment_Adr[1-PAGE] = ^visible page   }
  7071.  
  7072.             MOV SI,pa
  7073.             AND SI,3
  7074.             SHL SI,1
  7075.             ADD SI,OFFSET Segment_Adr-StartIndex*2
  7076.             LODSW         {AX:=Segment_Adr[pa] = ^source address}
  7077.  
  7078.             PUSH DS
  7079.             MOV DX,AX
  7080.             MOV BX,YMAX*LINESIZE+(LINESIZE-1)  {DX:BX = ^source data}
  7081.  
  7082.             MOV AX,YMAX   {AX = row counter  }
  7083.  
  7084.           oneLine2:
  7085.             STD           {move backwards!   }
  7086.             MOV SI,ES     {first scroll old contents upwards:   }
  7087.             MOV DS,SI     {DS=ES=visible graphic page }
  7088.             MOV SI,(YMAX-1)*LINESIZE+(LINESIZE-1)  {from last but one graphic row}
  7089.             MOV DI,YMAX*LINESIZE+(LINESIZE-1)      {to last graphic row     }
  7090.             MOV CX,YMAX*LINESIZE  {199 rows  }
  7091.             REP MOVSB
  7092.  
  7093.             MOV DS,DX     {now make new row visible:        }
  7094.             MOV SI,BX     {DS:SI=^row to move     }
  7095.             MOV CX,LINESIZE  {1 row  }
  7096.             REP MOVSB
  7097.  
  7098.             SUB BX,LINESIZE  {increase source pointer }
  7099.  
  7100.             {--- insertion to realize timing:}
  7101.             PUSH AX       {save all registers needed later     }
  7102.             PUSH BX
  7103.             PUSH DX
  7104.             PUSH ES
  7105.             MOV AX,SEG @DATA {restore TP's data segment         }
  7106.             MOV DS,AX
  7107.             CLD           {just to be sure!  }
  7108.           END;
  7109.           INC(counter);
  7110.           WHILE ClockTicks<t+counter*temp DO BEGIN END;
  7111.           ASM;
  7112.             POP ES
  7113.             POP DX
  7114.             POP BX
  7115.             POP AX
  7116.             {--- end of insertion}
  7117.  
  7118.             DEC AX        {all rows done?    }
  7119.             JNS oneLine2  {no, next row        }
  7120.  
  7121.             MOV DX,3CEh   {restore write mode 0        }
  7122.             MOV AX,4005h
  7123.             OUT DX,AX
  7124.             POP DS
  7125.           END;
  7126.          END
  7127.     ELSE BEGIN {scroll downwards   }
  7128.           ASM
  7129.             MOV DX,3C4h
  7130.             MOV AX,0F02h  {access all 4 planes at once          }
  7131.             OUT DX,AX
  7132.             MOV DX,3CEh
  7133.             MOV AX,4105h  {set write mode 1  }
  7134.             OUT DX,AX
  7135.  
  7136.             MOV SI,1
  7137.             SUB SI,PAGE
  7138.             AND SI,1
  7139.             SHL SI,1
  7140.             ADD SI,OFFSET Segment_Adr-StartIndex*2
  7141.             LODSW
  7142.             MOV ES,AX     {ES:=Segment_Adr[1-PAGE] = ^visible page   }
  7143.  
  7144.             MOV SI,pa
  7145.             AND SI,3
  7146.             SHL SI,1
  7147.             ADD SI,OFFSET Segment_Adr-StartIndex*2
  7148.             LODSW         {AX:=Segment_Adr[pa] = ^source address}
  7149.  
  7150.             PUSH DS
  7151.             MOV DX,AX
  7152.             MOV BX,0*LINESIZE  {DX:BX = ^source data}
  7153.  
  7154.             MOV AX,YMAX   {AX = row counter  }
  7155.  
  7156.           oneLine1:
  7157.             MOV SI,ES     {first scroll old contents upwards:   }
  7158.             MOV DS,SI     {DS=ES=visible graphic page }
  7159.             MOV SI,1*LINESIZE  {from graphic row 1}
  7160.             MOV DI,0*LINESIZE  {to graphic row 0  }
  7161.             MOV CX,YMAX*LINESIZE  {199 rows  }
  7162.             REP MOVSB
  7163.  
  7164.             MOV DS,DX     {now make new row visible:        }
  7165.             MOV SI,BX     {DS:SI=^row to move     }
  7166.             MOV CX,LINESIZE  {1 row  }
  7167.             REP MOVSB
  7168.  
  7169.             ADD BX,LINESIZE  {increase source pointer }
  7170.  
  7171.             {--- insertion to realize timing:}
  7172.             PUSH AX       {save all registers needed later     }
  7173.             PUSH BX
  7174.             PUSH DX
  7175.             PUSH ES
  7176.             MOV AX,SEG @DATA {restore TP's data segment         }
  7177.             MOV DS,AX
  7178.           END;
  7179.           INC(counter);
  7180.           WHILE ClockTicks<t+counter*temp DO BEGIN END;
  7181.           ASM;
  7182.             POP ES
  7183.             POP DX
  7184.             POP BX
  7185.             POP AX
  7186.             {--- end of insertion}
  7187.  
  7188.             DEC AX        {all rows done?    }
  7189.             JNS oneLine1  {no, next row        }
  7190.  
  7191.             MOV DX,3CEh   {restore write mode 0        }
  7192.             MOV AX,4005h
  7193.             OUT DX,AX
  7194.             POP DS
  7195.           END;
  7196.          END;
  7197.   END;
  7198.  
  7199.   PROCEDURE ScrollHorizontal(pa,time:WORD; left:BOOLEAN);
  7200.   { in: pa    = page, which contents will be made visible       }
  7201.   {     time  = time (in millisceconds) for this action (approx.)            }
  7202.   {     left  = TRUE/FALSE for: from left to right/vice versa   }
  7203.   {     1-PAGE= (visible) graphic page on which to draw         }
  7204.   {out: - }
  7205.   {rem: the contents of page "pa" has been copied to page 1-PAGE    }
  7206.   LABEL oneColumn1,oneColumn2;
  7207.   CONST n=Pred(LINESIZE);      {number of executions of the delay loop  }
  7208.   VAR counter:WORD;
  7209.       ClockTicks:LONGINT ABSOLUTE $40:$6C;
  7210.       t:LONGINT;
  7211.       temp:REAL;
  7212.   BEGIN
  7213.    t:=ClockTicks;
  7214.    counter:=0;
  7215.    temp:=0.0182*time/n;
  7216.    IF left
  7217.     THEN BEGIN {scroll to the left }
  7218.           ASM
  7219.             MOV DX,3C4h
  7220.             MOV AX,0F02h  {access all 4 planes at once          }
  7221.             OUT DX,AX
  7222.             MOV DX,3CEh
  7223.             MOV AX,4105h  {set write mode 1  }
  7224.             OUT DX,AX
  7225.  
  7226.             MOV SI,1
  7227.             SUB SI,PAGE
  7228.             AND SI,1
  7229.             SHL SI,1
  7230.             ADD SI,OFFSET Segment_Adr-StartIndex*2
  7231.             LODSW
  7232.             MOV ES,AX     {ES:=Segment_Adr[1-PAGE] = ^visible page   }
  7233.  
  7234.             MOV SI,pa
  7235.             AND SI,3
  7236.             SHL SI,1
  7237.             ADD SI,OFFSET Segment_Adr-StartIndex*2
  7238.             LODSW         {AX:=Segment_Adr[pa] = ^source address}
  7239.  
  7240.             PUSH DS
  7241.             MOV DX,AX
  7242.             MOV BX,0*LINESIZE+0 {DX:BX = ^source data}
  7243.  
  7244.             MOV AX,LINESIZE-1   {AX = column counter}
  7245.  
  7246.           oneColumn2:     {scroll old screen contents to the right:}
  7247.             MOV SI,ES
  7248.             MOV DS,SI     {DS = ES = visible graphic page }
  7249.             MOV SI,PAGESIZE-2
  7250.             MOV DI,PAGESIZE-1
  7251.             MOV CX,PAGESIZE-1
  7252.             STD
  7253.             REP MOVSB
  7254.             CLD
  7255.  
  7256.             MOV CX,SEG @DATA
  7257.             MOV DS,CX     {DS = ^TP's data}
  7258.             MOV CX,YMAX+1 {CX = row counter  }
  7259.  
  7260.             MOV SI,AX
  7261.             XOR DI,DI
  7262.             MOV BX,LINESIZE-1
  7263.             MOV DS,DX     {DS = ^source data}
  7264.  
  7265.           @oneRow:        {update first column:  }
  7266.             MOVSB
  7267.             ADD SI,BX     {position at next row:            }
  7268.             ADD DI,BX     {works, because BX+1=LINESIZE}
  7269.             LOOP @oneRow
  7270.  
  7271.             {--- insertion to realize timing:}
  7272.             PUSH AX       {save all registers needed later     }
  7273.             PUSH DX
  7274.             PUSH ES
  7275.             MOV AX,SEG @DATA {restore TP's data segment         }
  7276.             MOV DS,AX
  7277.           END;
  7278.           INC(counter);
  7279.           WHILE ClockTicks<t+counter*temp DO BEGIN END;
  7280.           ASM;
  7281.             POP ES
  7282.             POP DX
  7283.             POP AX
  7284.             {--- end of insertion}
  7285.  
  7286.             DEC AX        {all columns done?  }
  7287.             JNS oneColumn2  {no, next column      }
  7288.  
  7289.             MOV DX,3CEh   {restore write mode 0        }
  7290.             MOV AX,4005h
  7291.             OUT DX,AX
  7292.             POP DS
  7293.           END;
  7294.          END
  7295.     ELSE BEGIN {scroll to the right }
  7296.           ASM
  7297.             MOV DX,3C4h
  7298.             MOV AX,0F02h  {access all 4 planes at once          }
  7299.             OUT DX,AX
  7300.             MOV DX,3CEh
  7301.             MOV AX,4105h  {set write mode 1  }
  7302.             OUT DX,AX
  7303.  
  7304.             MOV SI,1
  7305.             SUB SI,PAGE
  7306.             AND SI,1
  7307.             SHL SI,1
  7308.             ADD SI,OFFSET Segment_Adr-StartIndex*2
  7309.             LODSW
  7310.             MOV ES,AX     {ES:=Segment_Adr[1-PAGE] = ^visible page   }
  7311.  
  7312.             MOV SI,pa
  7313.             AND SI,3
  7314.             SHL SI,1
  7315.             ADD SI,OFFSET Segment_Adr-StartIndex*2
  7316.             LODSW         {AX:=Segment_Adr[pa] = ^source address}
  7317.  
  7318.             PUSH DS
  7319.             MOV DX,AX
  7320.             MOV BX,0*LINESIZE+0 {DX:BX = ^source data}
  7321.  
  7322.             MOV AX,0      {AX = column counter}
  7323.  
  7324.           oneColumn1:     {scroll old screen contents to the left:}
  7325.             MOV SI,ES
  7326.             MOV DS,SI     {DS = ES = visible graphic page }
  7327.             MOV SI,1
  7328.             XOR DI,DI
  7329.             MOV CX,PAGESIZE-1
  7330.             REP MOVSB
  7331.  
  7332.             MOV CX,SEG @DATA
  7333.             MOV DS,CX     {DS = ^TP's data}
  7334.             MOV CX,YMAX+1 {CX = row counter  }
  7335.  
  7336.             MOV SI,AX
  7337.             MOV DI,LINESIZE-1
  7338.             MOV BX,LINESIZE-1
  7339.             MOV DS,DX     {DS = ^source data}
  7340.  
  7341.           @oneRow:        {update last column:    }
  7342.             MOVSB
  7343.             ADD SI,BX     {position at next row:            }
  7344.             ADD DI,BX     {works, because BX+1=LINESIZE}
  7345.             LOOP @oneRow
  7346.  
  7347.             {--- insertion to realize timing:}
  7348.             PUSH AX       {save all registers needed later     }
  7349.             PUSH DX
  7350.             PUSH ES
  7351.             MOV AX,SEG @DATA {restore TP's data segment         }
  7352.             MOV DS,AX
  7353.           END;
  7354.           INC(counter);
  7355.           WHILE ClockTicks<t+counter*temp DO BEGIN END;
  7356.           ASM;
  7357.             POP ES
  7358.             POP DX
  7359.             POP AX
  7360.             {--- end of insertion}
  7361.  
  7362.             INC AX        {all columns done?  }
  7363.             CMP AX,LINESIZE
  7364.             JB oneColumn1  {no, next column      }
  7365.  
  7366.             MOV DX,3CEh   {restore write mode 0        }
  7367.             MOV AX,4005h
  7368.             OUT DX,AX
  7369.             POP DS
  7370.           END;
  7371.          END;
  7372.   END;
  7373.  
  7374.   PROCEDURE CircleIn(pa,time:WORD);
  7375.   { in: pa    = page, which contents will be made visible       }
  7376.   {     time  = time (in millisceconds) for this action (approx.)            }
  7377.   {out: - }
  7378.   {rem: the contents of page "pa" has been copied to page 1-PAGE    }
  7379.   CONST centerX=XMAX DIV 2; {middle of screen}
  7380.         centerY=YMAX DIV 2;
  7381.         k=189;     {number of circles:= sqrt(centerX²+centerY²), rounded up}
  7382.         adjust=0.707106781; {compensation in diagonal direction = 1/sqrt(2) }
  7383.         n=TRUNC(k/adjust);  {number of executions of the delay loop  }
  7384.   VAR radqu,x,y,x0,y0,u1,u2,u3,u4,v1,v2,v3,v4:WORD;
  7385.       counter:WORD;
  7386.       ClockTicks:LONGINT ABSOLUTE $40:$6C;
  7387.       t:LONGINT;
  7388.       radius,temp:REAL;
  7389.   BEGIN
  7390.    t:=ClockTicks;
  7391.    counter:=0;
  7392.    temp:=0.0182*time/n;
  7393.    x0:=centerX + StartVirtualX;
  7394.    y0:=centerY + StartVirtualY;
  7395.    {unfortunately, FOR true_radius:=1 TO k STEP 1/adjust isn't possible in TP }
  7396.    radius:=0.0;
  7397.    REPEAT
  7398.     radqu:=TRUNC(sqr(radius));
  7399.     FOR x:=0 TO TRUNC(radius/sqrt(2)) DO {compute octant    }
  7400.      BEGIN
  7401.       y:=TRUNC(sqrt(radqu-sqr(x))); {Pythagorean proposition}
  7402.       u1:=x0-x; v1:=y0-y;           {use axial- and point symmetrie      }
  7403.       u2:=x0+x; v2:=y0+y;
  7404.       u3:=x0-y; v3:=y0-x;
  7405.       u4:=x0+y; v4:=y0+x;
  7406.       PutPixel(u1,v1,PageGetPixel(u1,v1,pa));
  7407.       PutPixel(u1,v2,PageGetPixel(u1,v2,pa));
  7408.       PutPixel(u2,v1,PageGetPixel(u2,v1,pa));
  7409.       PutPixel(u2,v2,PageGetPixel(u2,v2,pa));
  7410.       PutPixel(u3,v3,PageGetPixel(u3,v3,pa));
  7411.       PutPixel(u3,v4,PageGetPixel(u3,v4,pa));
  7412.       PutPixel(u4,v3,PageGetPixel(u4,v3,pa));
  7413.       PutPixel(u4,v4,PageGetPixel(u4,v4,pa));
  7414.      END;
  7415.     radius:=radius+adjust;
  7416.     INC(counter);
  7417.     WHILE ClockTicks<t+counter*temp DO BEGIN END;
  7418.    UNTIL radius>=k;
  7419.   END;
  7420.  
  7421. BEGIN {of FadeIn}
  7422.  IF (pa<>0) AND (pa<>1) AND (pa<>BACKGNDPAGE)
  7423.   THEN Error:=Err_InvalidPageNumber
  7424.   ELSE CASE style OF
  7425.         Fade_Squares :WipeIn(pa,ti);
  7426.         Fade_Moiree1..Fade_Moiree14:Chaos(pa,ti,style+1-Fade_Moiree1);
  7427.         Fade_SweepInFromTop:    SweepVertical(pa,ti,TRUE);
  7428.         Fade_SweepInFromBottom: SweepVertical(pa,ti,FALSE);
  7429.         Fade_SweepInFromLeft:   SweepHorizontal(pa,ti,TRUE);
  7430.         Fade_SweepInFromRight:  SweepHorizontal(pa,ti,FALSE);
  7431.         Fade_ScrollInFromTop:   ScrollVertical(pa,ti,TRUE);
  7432.         Fade_ScrollInFromBottom:ScrollVertical(pa,ti,FALSE);
  7433.         Fade_ScrollInFromLeft:  ScrollHorizontal(pa,ti,TRUE);
  7434.         Fade_ScrollInFromRight: ScrollHorizontal(pa,ti,FALSE);
  7435.         Fade_Circles : CircleIn(pa,ti);
  7436.         else Error:=Err_InvalidFade
  7437.        END;
  7438. END;
  7439.  
  7440.  
  7441. PROCEDURE InitRoutines;
  7442. { in: - }
  7443. {out: SpriteN[],SPRITEAD[] have been initialized to "completely empty"     }
  7444. {     NextSprite[] has been set in such a way that every sprite is its   }
  7445. {     own successor}
  7446. {     PAGE, PAGEADR have been set to graphic page 0         }
  7447. {     BACKGNDADR has been set to the background page        }
  7448. {     SCROLLADR has been set to the scrollable background page   }
  7449. {     BACKGROUNDMODE has been set to STATIC  }
  7450. {     tile #0 has been set as default tile for scrollable background     }
  7451. {     StartVirtualX,StartVirtualY =0 (that is: virtual = absolute coord.)}
  7452. {     oldMode = old graphicmode  }
  7453. {     Error has been set, if no VGA-card could be found in the system    }
  7454. {     CycleTime = 0, that is: no min. time for animation cycle           }
  7455. {     Color = 15, that is: white }
  7456. {     ActualColors = default color palette of mode $13 }
  7457. {     CRTAddress = port address of CRT-address register}
  7458. {     StatusReg  = port address of state register     }
  7459. {rem: This procedure should be called only once - namely in the very     }
  7460. {     beginning - for initializing the whole package properly                            }
  7461. VAR i:WORD;
  7462.  
  7463.     FUNCTION IsVGA:BOOLEAN;
  7464.     BEGIN
  7465.      WITH Regs DO
  7466.       BEGIN
  7467.        AX:=$1A00;
  7468.        Intr($10,Regs);
  7469.        IsVGA:=(AL=$1A) AND    {VGA's identify-adapter-function supported? }
  7470.               ( (BL=7) OR (BL=8) )  {monochrome or color VGA - adapter}
  7471.       END;
  7472.     END;
  7473.  
  7474. BEGIN
  7475.  IF IsVGA THEN Error:=Err_None
  7476.           ELSE Error:=Err_NoVGA;
  7477.  
  7478.  SetCycleTime(0);
  7479.  
  7480.  fillchar(SpriteN,sizeof(SpriteN),0);
  7481.  fillchar(SPRITEAD,sizeof(SPRITEAD),0);
  7482.  
  7483.  FOR i:=0 TO LoadMAX DO NextSprite[i]:=i;
  7484.  
  7485.  BACKGNDADR:=Segment_Adr[BACKGNDPAGE];  {segment address of background page }
  7486.  
  7487.  PAGE:=0;   {page to be drawn upon                }
  7488.  PAGEADR:=Segment_Adr[PAGE];
  7489.  SCROLLADR:=Segment_Adr[SCROLLPAGE];
  7490.  SetBackgroundMode(STATIC);
  7491.  SetOffscreenTile(0);
  7492.  
  7493.  StartVirtualX:=0; StartVirtualY:=0;    {virtual = absolute coordinates  }
  7494.  Color:=white;            {set actual drawing color to white }
  7495.  regs.ah:=$f; intr($10,regs); oldMode:=regs.al;
  7496.  
  7497.  ActualColors:=DefaultColors;
  7498.  {SetShadowTab(Schatten) isn't needed, as the default values are set already}
  7499.  
  7500.  ASM  {see if we are using color-/monochorme display}
  7501.    MOV DX,3CCh  {use output register:     }
  7502.    IN AL,DX
  7503.    TEST AL,1    {is it a color display?    }
  7504.    MOV DX,3D4h
  7505.    JNZ @L1      {yes  }
  7506.    MOV DX,3B4h  {no  }
  7507.   @L1:          {DX=3B4h/3D4h = CRTAddress-register for monochrome/color}
  7508.    MOV CRTAddress,DX
  7509.    ADD DX,6     {DX=3BAh/3DAh = state register for monochrome/color }
  7510.    MOV StatusReg,DX
  7511.  END; {of ASM}
  7512. END;
  7513.  
  7514. PROCEDURE CloseRoutines;
  7515. { in: oldMode = old graphicmode, to which we'll switch back             }
  7516. {out: - }
  7517. BEGIN
  7518.  regs.al:=oldMode; regs.ah:=0; intr($10,regs);
  7519. END;
  7520.  
  7521. FUNCTION GetErrorMessage:STRING;
  7522. { in: Error = number of the occurred error    }
  7523. {out: the error described in words}
  7524. BEGIN
  7525.  CASE Error OF
  7526.   Err_None:GetErrorMessage:='No Error';
  7527.   Err_NotEnoughMemory:GetErrorMessage:='Not enough memory available on heap';
  7528.   Err_FileIO:GetErrorMessage:='I/O-error with file';
  7529.   Err_InvalidSpriteNumber:GetErrorMessage:='Invalid sprite number used';
  7530.   Err_NoSprite:GetErrorMessage:='No (or corrupted) sprite file';
  7531.   Err_InvalidPageNumber:GetErrorMessage:='Invalid page number used';
  7532.   Err_NoVGA:GetErrorMessage:='No VGA-card found';
  7533.   Err_NoPicture:GetErrorMessage:='No (or corrupted) picture file';
  7534.   Err_InvalidPercentage:GetErrorMessage:='Percentage value must be 0..100';
  7535.   Err_NoTile:GetErrorMessage:='No (or corrupted) tile/sprite file';
  7536.   Err_InvalidTileNumber:GetErrorMessage:='Invalid tile number used';
  7537.   Err_InvalidCoordinates:GetErrorMessage:='Invalid coordinates used';
  7538.   Err_BackgroundToBig:GetErrorMessage:='Background to big for tile-buffer';
  7539.   Err_InvalidMode:GetErrorMessage:='Only STATIC or SCROLLING allowed here';
  7540.   Err_InvalidSpriteLoadNumber:GetErrorMessage:='Invalid spriteload number used';
  7541.   Err_NoPalette:GetErrorMessage:='No (or corrupted) palette file';
  7542.   Err_PaletteWontFit:GetErrorMessage:='Attempt to write beyond palette end';
  7543.   Err_InvalidFade:GetErrorMessage:='Invalid fade style used';
  7544.   ELSE GetErrorMessage:='Unknown error';
  7545.  END;
  7546. END;
  7547.  
  7548.  
  7549. BEGIN
  7550.  
  7551.  InitRoutines;
  7552.  
  7553. END.
  7554.